產生文件
File file = new File("abc.txt"); if(!file.exists()){ System.out.println(file.exists()); file.createNewFile(); } System.out.println(file.getAbsolutePath());
關於臨時文件
1).放在指定目錄,此時已項目為默認目錄
File file = File.createTempFile("defaultTmp","tmpp",new File("./")); System.out.println(file.getAbsolutePath());
--------------------------------------------------
/Users/liuxin/work/workspace2/learnJava/./defaultTmp5487876755372558069tmpp
2).放在默認目錄
File file = File.createTempFile("defaultTmp",".tmpp"); System.out.println(file.getAbsolutePath()); ------------------------------------------------------------------------ /var/folders/f9/x95426d95ng7wgy7f7yccj3w0000gn/T/defaultTmp1175907986750188229tmpp
3).虛擬機退出時刪除臨時文件
File file = File.createTempFile("defaultTmp",".tmpp"); file.deleteOnExit(); System.out.println(file.getAbsolutePath());
4).關於后綴
File testFile = new File("testFile"); if(!testFile.exists()){ testFile.mkdirs(); } testFile.deleteOnExit(); File file = File.createTempFile("defaultTmp","tmpp"); System.out.println(file.getAbsolutePath()); File file2 = File.createTempFile("defaultTmp2",".tmpp",testFile); System.out.println(file2.getAbsolutePath()); File file3 = File.createTempFile("defaultTmp3",null,testFile); System.out.println(file3.getAbsolutePath()); ======================================= /var/folders/f9/x95426d95ng7wgy7f7yccj3w0000gn/T/defaultTmp5296324826431648502tmpp /Users/liuxin/work/workspace2/learnJava/testFile/defaultTmp21551336802243345058.tmpp /Users/liuxin/work/workspace2/learnJava/testFile/defaultTmp33930717872920538957.tmp
操作目錄
1.查看目錄下的所有文件
File currentFile = new File("."); System.out.println(currentFile.getAbsolutePath()); for(File file : currentFile.listFiles()){ System.out.println(file.getName()); }
getParent()的使用方法
File file = new File("."); System.out.println(file.getAbsolutePath()); System.out.println(new File(file.getAbsolutePath()).getParent()); ----------------------- /Users/liuxin/work/workspace2/learnJava/. /Users/liuxin/work/workspace2/learnJava getParent()只有在定義文件時有路徑時才起作用.
15.2 流
字節流主要由InputStream和OutputStream作為基類,而字符流則主要由Reader和Writer作為基類.
字節流操作的數據單元是8位的字節,而字符流操作的數據單元是16位的字符
節點流和處理流,節點流是低級流,直接跟數據源相接.處理流(也叫包裝流)把節點流包裝了一層,屬於修飾器設計模式.
處理流的功能主要體現在以下兩個方面:
1.性能的提高:主要以增加緩沖的方式來提高輸入輸出的效率
2.操作的便捷:處理流可能提供了一系列便捷的方法來一次輸入輸出大批量的內容,而不是輸入/輸出一個或多個水滴
處理流可以嫁接在任何已存在的流的基礎上.
15.3.1 InputStream和Reader
InputStream和Reader是所有輸入流的抽象基類,本身並不能創建實例來執行.
讀取文件的demo
public static void testFile() throws IOException{ // 創建字節輸入流 FileInputStream fis = new FileInputStream("a.txt"); // 創建一個長度為1024的“竹筒” byte[] bbuf = new byte[1024]; // 用於保存實際讀取的字節數 int hasRead = 0; // 使用循環來重復“取水”過程 while ((hasRead = fis.read(bbuf)) > 0 ) { // 取出“竹筒”中水滴(字節),將字節數組轉換成字符串輸入! System.out.print(new String(bbuf , 0 , hasRead )); } // 關閉文件輸入流,放在finally塊里更安全 fis.close(); }
關於fileFilter
File file = new File("."); String[] nameList = file.list((dir, name) -> name.endsWith(".java") || new File(name).isDirectory()); for(String name : nameList) { System.out.println(name); } ================================ .settings a bin lib result sqlModify src testFile
15.2 理解java的IO流
用字節流讀取文件並打印
public static void testFile2() throws IOException{ // 創建字節輸入流 FileInputStream fis = new FileInputStream("a.txt"); //讀取一個字節,返回的是int,所以要用char強轉 System.out.println((char)fis.read()); // 創建一個長度為1024的“竹筒” byte[] bbuf = new byte[1024]; //讀取5個字節,放在bbuf的index為10的位置 fis.read(bbuf,10,10); // 從bbuf中index為10的位置讀取5個字節構成字符串 System.out.println(new String(bbuf , 10, 5)); //轉換成String后,byte[]不變 System.out.println(new String(bbuf , 10, 5)); // 用於保存實際讀取的字節數 int hasRead = 0; // 使用循環來重復“取水”過程 while ((hasRead = fis.read(bbuf)) > 0 ) { //重新賦值后,byte[]會改變 System.out.println(new String(bbuf , 10, 5)); // 取出“竹筒”中水滴(字節),將字節數組轉換成字符串輸入! System.out.println("=============================================="); String test = new String(bbuf , 0 , hasRead ); System.out.println(test); System.out.println("=================="+new String(bbuf , 0 , hasRead ).length()+"============================"); } // 關閉文件輸入流,放在finally塊里更安全 fis.close(); }
打印結果如下
< ?xml ?xml ncodi ============================================== on="1.0" encoding="UTF-8"?> <projectDescription> <name>learnJava</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> 我很好啊,看看需不要utf-8 ==================373============================
public static void testFile3() { try( // 創建字符輸入流 FileReader fr = new FileReader("a.txt") ){ // 創建一個長度為32的“竹筒” char[] cbuf = new char[32]; System.out.println(fr.read(cbuf,0,32)); System.out.println(new String(cbuf)); // 用於保存實際讀取的字符數 int hasRead = 0; // 使用循環來重復“取水”過程 while ((hasRead = fr.read(cbuf)) > 0 ) { // 取出“竹筒”中水滴(字符),將字符數組轉換成字符串輸入! // System.out.print(new String(cbuf , 0 , hasRead)); System.out.print(new String(cbuf)); } } catch (IOException ex) { ex.printStackTrace(); } }
outPutStream 和 Writer
public static void testFileWriter() {
//這里a.txt可以不存在,如果不存在回自動生成,但是沒搞明白怎么設置編碼格式 try(FileWriter fw = new FileWriter("a.txt")) { fw.write("錦瑟 - 李商隱\r\n"); fw.write("錦瑟無端五十弦,一弦一柱思華年。\r\n"); fw.write("庄生曉夢迷蝴蝶,望帝春心托杜鵑。\r\n"); fw.write("滄海月明珠有淚,藍田日暖玉生煙。\r\n"); fw.write("此情可待成追憶,只是當時已惘然。\r\n"); } catch (IOException ioe) { ioe.printStackTrace(); } }
15.4 輸入輸出流體系
處理流:構造參數是已經存在的流.
節點流:構造參數是物理IO節點
處理流的用法
public static void PrintStream() { try( FileOutputStream fos = new FileOutputStream("a.txt"); PrintStream ps = new PrintStream(fos)) { // 使用PrintStream執行輸出 ps.println("普通字符串"); // 直接使用PrintStream輸出對象 ps.println(new LearnIO()); } catch (IOException ioe) { ioe.printStackTrace(); } }
輸出結果
普通字符串
learnIO.LearnIO@677327b6
在使用了處理流包裝了底層節點之后,關閉輸入/輸出流資源時,只要關閉最上層的處理流即可。關閉最上層的處理流時,系統會自動關閉被該處理流包裝的節點流。
字符串流的用法,似乎沒什么用,等發現用處再補充
public static void stringNodeTest() { String src = "從明天起,做一個幸福的人\n" + "喂馬,劈柴,周游世界\n" + "從明天起,關心糧食和蔬菜\n" + "我有一所房子,面朝大海,春暖花開\n" + "從明天起,和每一個親人通信\n" + "告訴他們我的幸福\n"; char[] buffer = new char[32]; int hasRead = 0; try( StringReader sr = new StringReader(src)) { // 采用循環讀取的訪問讀取字符串 while((hasRead = sr.read(buffer)) > 0) { System.out.print(new String(buffer ,0 , hasRead)); } } catch (IOException ioe) { ioe.printStackTrace(); } try( // 創建StringWriter時,實際上以一個StringBuffer作為輸出節點 // 下面指定的20就是StringBuffer的初始長度 StringWriter sw = new StringWriter()) { // 調用StringWriter的方法執行輸出 sw.write("有一個美麗的新世界,\n"); sw.write("她在遠方等我,\n"); sw.write("哪里有天真的孩子,\n"); sw.write("還有姑娘的酒窩\n"); System.out.println("----下面是sw的字符串節點里的內容----"); // 使用toString()方法返回StringWriter的字符串節點的內容 System.out.println(sw.toString()); } catch (IOException ex) { ex.printStackTrace(); } }
15.4.3 轉換流
InputStreamReader :將字節輸入流轉換成字符輸入流
OutputStreamWriter:將字節輸出流轉換成字符輸出流
經過測試,下面這個程序是有一定問題的,用輸入法的時候,會重復輸出好多遍
public static void KeyinTest() { try( // 將Sytem.in對象轉換成Reader對象 InputStreamReader reader = new InputStreamReader(System.in); // 將普通Reader包裝成BufferedReader BufferedReader br = new BufferedReader(reader)) { String line = null; // 采用循環方式來一行一行的讀取 while ((line = br.readLine()) != null) { // 如果讀取的字符串為"exit",程序退出 if (line.equals("exit")) { System.exit(1); } // 打印讀取的內容 System.out.println("輸入內容為:" + line); } } catch (IOException ioe) { ioe.printStackTrace(); } }
15.4.4 推回輸入流
PushbackInputStream和PushbackReader
用unread方法將內容推回到緩沖區,從而允許重復讀取剛剛讀取的內容.
使用推回流,輸出某分割符號之前的內容。但其實完全不必這么做,讀出來判斷就好了。
public static void pushbackTest() { try( // 創建一個PushbackReader對象,指定推回緩沖區的長度為64 PushbackReader pr = new PushbackReader(new FileReader("a.txt") , 64)) { char[] buf = new char[32]; // 用以保存上次讀取的字符串內容 String lastContent = ""; int hasRead = 0; // 循環讀取文件內容 while ((hasRead = pr.read(buf)) > 0) { // 將讀取的內容轉換成字符串 String content = new String(buf , 0 , hasRead); int targetIndex = 0; // 將上次讀取的字符串和本次讀取的字符串拼起來, // 查看是否包含目標字符串, 如果包含目標字符串 if ((targetIndex = (lastContent + content) .indexOf("測試")) > 0) { // 將本次內容和上次內容一起推回緩沖區 pr.unread((lastContent + content).toCharArray()); // 重新定義一個長度為targetIndex的char數組 if(targetIndex > 32) { buf = new char[targetIndex]; } // 再次讀取指定長度的內容(就是目標字符串之前的內容) pr.read(buf , 0 , targetIndex); // 打印讀取的內容 System.out.print(new String(buf , 0 ,targetIndex)); System.exit(0); } else { // 打印上次讀取的內容 System.out.print(lastContent); // 將本次內容設為上次讀取的內容 lastContent = content; } } } catch (IOException ioe) { ioe.printStackTrace(); } }
system類里提供了三個重定向標准輸入輸出的方法。
setErr(PrintStream err)
setIn(InputStream in)
setOut(PrintStream out)
程序可通過重定向標准輸出流,將System.out的輸出重定向到文件輸出.
15.6 Java虛擬機讀寫其他進程的數據
使用Runtime對象的exec()方法可以運行平台上的其他程序,該方法產生一個Process對象,Process對象代表由該Java程序啟動的子進程.Process類提供了如下三個方法,用於讓程序和其子進程進行通信.
InputStream getErrorStream():獲取子進程的錯誤流,
InputStream getInputStream(): 獲取子進程的輸入流.
OutputStream getOutputStream(): 獲取子進程的輸出流.
這里的輸入流和輸出流是相對於該java程序(注意,不是子程序)而言的.這里的子進程相當於物理節點
注意,這里用的是getErrorStream(),因為這不是子進程向本進程傳遞的數據,所以屬於其他,要用ErrorStream
public static void readFromProcess() throws IOException { // 運行javac命令,返回運行該命令的子進程 Process p = Runtime.getRuntime().exec("java -version"); try( // 以p進程的錯誤流創建BufferedReader對象 // 這個錯誤流對本程序是輸入流,對p進程則是輸出流 BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))) { String buff = null; // 采取循環方式來讀取p進程的錯誤輸出 while((buff = br.readLine()) != null) { System.out.println(buff); } } }
java version "1.8.0_51" Java(TM) SE Runtime Environment (build 1.8.0_51-b16) Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)
java兩個程序之間傳遞信息:這端代碼我沒跑起來,待研究
public class WriteToProcess { public static void main(String[] args) throws IOException { // 運行java ReadStandard命令,返回運行該命令的子進程 Process p = Runtime.getRuntime().exec("java ReadStandard"); try( // 以p進程的輸出流創建PrintStream對象 // 這個輸出流對本程序是輸出流,對p進程則是輸入流 PrintStream ps = new PrintStream(p.getOutputStream())) { // 向ReadStandard程序寫入內容,這些內容將被ReadStandard讀取 ps.println("普通字符串"); ps.println(new WriteToProcess()); } } } // 定義一個ReadStandard類,該類可以接受標准輸入, // 並將標准輸入寫入out.txt文件。 class ReadStandard { public static void main(String[] args) { try( // 使用System.in創建Scanner對象,用於獲取標准輸入 Scanner sc = new Scanner(System.in); PrintStream ps = new PrintStream( new FileOutputStream("out.txt"))) { // 增加下面一行將只把回車作為分隔符 sc.useDelimiter("\n"); // 判斷是否還有下一個輸入項 while(sc.hasNext()) { // 輸出輸入項 ps.println("鍵盤輸入的內容是:" + sc.next()); } } catch(IOException ioe) { ioe.printStackTrace(); } } }
15.7 RandomAccessFile
RandomAccessFile可以自由訪問文件的任意位置,所以如果只需要訪問文件部分內容,而不是把文件從頭讀到尾,使用RamdomAccessFile將是更好的選擇.但是局限是,它只能讀寫文件,不能讀寫其他IO節點.
getFilePointer():返回文件記錄指針的當前位置
seek(long pos):將文件記錄指針定位到pos位置
public static void randomAccessFileTest(){ try( RandomAccessFile raf = new RandomAccessFile( "newFile.txt" , "r")) { // 獲取RandomAccessFile對象文件指針的位置,初始位置是0 System.out.println("RandomAccessFile的文件指針的初始位置:" + raf.getFilePointer()); // 移動raf的文件記錄指針的位置 raf.seek(30); byte[] bbuf = new byte[1024]; // 用於保存實際讀取的字節數 int hasRead = 0; // 使用循環來重復“取水”過程 while ((hasRead = raf.read(bbuf)) > 0 ) { // 取出“竹筒”中水滴(字節),將字節數組轉換成字符串輸入! System.out.print(new String(bbuf , 0 , hasRead )); } } catch (IOException ex) { ex.printStackTrace(); } }
RandomAccessFile 依然不能向文件的指定位置插入內容,如果直接將文件記錄指針移動到中間某位置后開始輸出,則新輸出的內容會覆蓋文件中原有的內容,如果需要向指定位置插入內容,程序需要先把插入點后面的內容讀入緩沖區,等把需要插入的數據寫入文件后,再將緩沖區的內容追加到文件的后面.
public static void insert(String fileName , long pos , String insertContent) throws IOException { File tmp = File.createTempFile("tmp" , null); tmp.deleteOnExit(); try( RandomAccessFile raf = new RandomAccessFile(fileName , "rw"); // 使用臨時文件來保存插入點后的數據 FileOutputStream tmpOut = new FileOutputStream(tmp); FileInputStream tmpIn = new FileInputStream(tmp)) { raf.seek(pos); // ------下面代碼將插入點后的內容讀入臨時文件中保存------ byte[] bbuf = new byte[64]; // 用於保存實際讀取的字節數 int hasRead = 0; // 使用循環方式讀取插入點后的數據 while ((hasRead = raf.read(bbuf)) > 0 ) { // 將讀取的數據寫入臨時文件 tmpOut.write(bbuf , 0 , hasRead); } // ----------下面代碼插入內容---------- // 把文件記錄指針重新定位到pos位置 raf.seek(pos); // 追加需要插入的內容 raf.write(insertContent.getBytes()); // 追加臨時文件中的內容 while ((hasRead = tmpIn.read(bbuf)) > 0 ) { raf.write(bbuf , 0 , hasRead); } } }
如果僅僅是追加內容,就簡單多了
public static void main(String[] args) { try( //以讀、寫方式打開一個RandomAccessFile對象 RandomAccessFile raf = new RandomAccessFile("out.txt" , "rw")) { //將記錄指針移動到out.txt文件的最后 raf.seek(raf.length()); raf.write("追加的內容!\r\n".getBytes()); } catch (IOException ex) { ex.printStackTrace(); } }
15.8 對象序列化(serialize)
對象的序列化指將一個java對象寫入IO流中,與此對應的是,對象的反序列化(Deserialize)則指從IO流中恢復該java對象.
如果需要讓某個對象支持序列化機制,那么必須讓它的類是可序列化的,必須實現如下兩個接口之一
Serializable
Externalizable
程序創建的每個JavaBean類都要實現Serializable
public static void writeObject() { try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("obj.txt"));) { LearnIO learnIO = new LearnIO("12334444444"); oos.writeObject(learnIO); } catch (IOException ex) { // TODO: handle exception ex.printStackTrace(); } }
//但是,如果如下
LearnIO learnIO = new LearnIO("12334444444");
oos.writeObject(learnIO);
learnIO.test="abcdetc";
//不會寫入abcdetc,因為不會序列化號碼相同的東西
oos.writeObject(learnIO);
反序列化
public static void readObject(){ try( ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt")); ){ LearnIO learnIO = (LearnIO)ois.readObject(); System.out.println(learnIO.test); }catch (Exception e) { // TODO: handle exception e.printStackTrace(); } }
反序列化機制無須通過構造器來初始化java對象.
如果使用序列化機制向文件中寫入多個Java對象,使用反序列化機制恢復對象時必須按實際寫入的順序讀取.
當一個可序列化類有多個父類時(包括直接父類和間接父類),這些父類要么有無參數的構造器,要么也是可序列化的--否則反序列化時將拋出InvalidClassException異常.如果父類是不可序列化的,只是帶有無參數的構造器,則該父類中定義的成員變量值不會序列化到二進制流中.
15.8.3 對象引用的序列化
如果成員變量是引用類型,那么這個引用類型的類必須是可序列化的.
屬於遞歸序列化
有static修飾和transient修飾的變量不會被序列化
15.8.4 自定義序列化
在實例變量前面使用transient關鍵字修飾,可以指定java序列化時無須理會該實例變量.
更詳細的自定義序列化方法是重寫writeObject和readObject方法.
15.8.5 另一種自定義序列化機制
實現Externalizable