對於java的文件讀寫是咱們必須使用的一項基本技能,所以瞭解其中的原理,字節流和字符流的本質有着重要的意義。html
1 方式: 字節流 Byte 和 字符流 Char 2 方向: 輸入 Input 和 輸出 Output ; 讀 Reader 和 寫 Writer 3 源: 字符串 String, 數組 Array, 對象 Object, 文件 File, 通道 Channel, 管道 Pipe, 過濾器 Filter,控制檯 Console, 網絡 Network Link ;
一切可產生/接收數據的 Data 4 性能: 帶緩衝和不帶緩衝的 5 行爲: Readable, Appendable, Closable, Flushable, Serializable/Externalizable
方式、方向、源、行爲四個維度能夠組合出各類特性的IO流。
JavaIO框架採用裝飾器模式,能夠在現有IO流的基礎上添加新的IO特性,生成更強功能的IO流,體現了讀寫能力的可擴展性。 好比要構造一個帶緩衝的文本文件讀取流:
File -> FileInputStream -> InputStreamReader -> FileReader -> BufferedFileReader,
其中文本:字符流, 文件:源, 讀:輸入方向, 帶緩衝的:性能。 讀寫耗時的字符輸入輸出流可使用緩衝來提高性能,好比文件讀寫、網絡流讀寫等。
這是因爲每一個待讀寫的字符都要通過讀寫字節、字節/字符轉換兩個操做。
緩衝其實是將大量單個的讀寫操做轉換爲一個大的單個的批量讀寫操做。
1 字節輸入流:InputStream, BufferedInputStream, FileInputStream, ByteArrayInputStream, PipedInputStream, FilterInputStream, DataInputStream, ObjectInputStream; 2 字節輸出流: OuputStream, BufferedOuputStream, FileOuputStream, ByteArrayOuputStream, PipedOuputStream, FilterOuputStream, DataOuputStream, ObjectOuputStream, PrintStream;
1 字符輸入流: Reader, BufferedReader, FileReader, CharArrayReader, PipedReader, FilterReader, StringReader, LineNumberReader; 2 字符輸出流: Writer, BufferedWriter, FileWriter, CharArrayWriter, PipedWriter, FilterWriter, StringWriter, PrintWriter;
1 InputStreamReader:輸入字節流轉化爲輸入字符流 2 OutStreamWriter: 輸出字符流轉化爲輸出字節流
多個類實現同一個接口,而且這些實現類均持有一個該接口的引用,經過構造器傳入,從而能夠在這些實現類的基礎上任意動態地組合疊加,構造出所需特性的實現來。裝飾器模式能夠實現數學公式/邏輯公式運算函數。java
在IO流的實現類中,一般有一個 Char[],Byte[], CharBuffer, StringBuffer, ByteBuffer的成員數組或成員對象。將成員數組/對象裏的數據寫到方法裏給定的參數數組或返回值,即爲讀實現;將方法裏給定的參數數組寫到成員數組或成員對象裏,即爲寫實現。讀寫數據本質上就是數據在不一樣源之間的拷貝實現。
IO流一般是會實現讀寫單個字節/字符、讀寫字節數組/字符數組、跳過若干字節/字符的功能。成員 (next,pos,mark,count) 用於標識數據讀寫位置;mark 與 reset 方法組合可實現重複讀寫。linux
1 要實現一個自定義的 Reader, 可參考 StringReader 實現;StringReader 能夠將字符串做爲字符流來讀取,內部成員爲String;
而 StringWriter 內部成員爲線程安全的 StringBuffer。二者均要考慮併發讀寫的問題,使用一個 Object 鎖進行互斥訪問。 2 FileReader 是文本文件讀取流,經過繼承 InputStreamReader, 轉化 FileInputStream 而得。
因文件是對磁盤字節數據的抽象,所以要得到人類可讀的文本,必然存在從字節流到字符流的轉換。 3 InputStreamReader 使用 StreamDecoder 將字節流轉換爲指定字符編碼的字符流,後續讀操做直接委託 StreamDecoder 讀取;
StreamDecoder 是一個字節解碼器, 是 Reader 實現類。
OutputStreamWriter 使用 StreamEncoder 將字符流轉換爲指定字符編碼的字節流,
後續寫操做直接委託 StreamEncoder 寫入底層 OutputStream;StreamEncoder 是一個 Writer 實現類。 4 FilterReader, FilterWriter, FilterInputStream, FilterOutputStream: IO流的抽象類,分別持有一個[Reader,Writer,InputStream, OutputStream],
自定義 IO 流能夠經過繼承 FilterXXX 來實現。CommonIO庫裏 ProxyReader, ProxyWriter 分別提供了 Reader, Writer 的一個示例實現,
java.io.DataInputStream 提供了 FilterInputStream 的一個示例實現,java.io.PrintStream 提供了 FilterOutputStream 的一個示例實現。 5 BufferedReader: 提供了緩衝讀及讀行的功能。
BufferedReader 主要成員爲 [Reader in, char[defaultsize=8192] cb, int nextChar, int nChars, int markedChar];
nextChar 指示將要讀取的字符位置, nChars 指示能夠讀取字符的終止位置,
markedChar 表示被標記的將要讀取字符的位置;
BufferedReader 從字符流 in 中讀取字符預存入緩衝字符數組 cb 中,而後在須要的時候從 cb 中讀取字符。
BufferedReader 的實現由 fill 和 read, readLine 共同完成。
當緩衝字符數組的字符數不足於填充要讀取的參數數組時,就會使用 fill 方法從輸入流 in 中讀取數據再進行填充;6 PipedReader: 管道輸入流,必須從 PipedWriter 中讀取。
底層實現有兩個線程、兩個位置索引以及一個字符數組。
兩個線程分別是讀線程 readSide 和 寫線程 writeSide, 兩個位置索引分別用於指示字符數組中將要寫的位置和將要讀的位置。
字符數組是一個循環隊列,默認大小爲 1024 chars 。初始化時必須將 PipedReader 的輸入鏈接至 PipedWriter 。
讀實現方法中,便是將底層字符數組拷貝到方法中的參數數組。PipedWriter 的實現相對簡單,其輸出鏈接至 PipedReader;
PipedWriter 的寫就是 PipedReader 的讀,所以 PipedWriter 的方法實現委託給 PipedReader 引用的方法。
1 文件相關的類: File, RandomAccessFile, FileDescriptor, FileChannel, FilePermission, FilePermissionCollection, FileFilter, FileSystem, UnixFileSystem ; 2 文件 File: 惟一性路徑標識【目錄路徑/文件名[.文件擴展名]】、文件元信息【描述符、類型、大小、建立時間、最近訪問時間、節點信息】、文件訪問權限【讀/寫/執行組合】等;
File 的操做會先使用 SecurityManager 檢查訪問權限,而後經過 FileSystem 判斷和操做。 3 構造能夠實際讀寫的文件輸入輸出流須要使用 FileInputStream, FileOutputStream, FileReader, FileWriter, BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream 來包裝 File ,
好比 new BufferedReader(new FileReader(new File(filename))) 。 4 FileChannel: 操做文件內容的通道;FileChannel 是線程安全的,使用 java.nio 引入的高性能機制來實現。 5 文件描述符 FileDescriptor : 用於操做打開的文件、網絡socket、字節流的惟一標識句柄;
其成員是一個整型數 fd 和一個原子計數器 AtomicInteger useCount ;
標準輸入流 System.in.fd = 0 , 標準輸出流 System.out.fd = 1 , 標準錯誤流 System.err.fd = 2 6 文件訪問權限 FilePermission: 提供了【文件可進行的操做 actions 與位掩碼錶示 mask 】之間的轉化方法。
文件可進行的操做 actions 在類 SecurityConstants 的常量中定義;
mask 是一個整型數,表示文件的執行(0x01)/寫(0x02)/讀(0x04)/刪(0x08)/讀連接(0x10)/無(0x00) 等權限組合。 7 FileSystem:所使用的操做系統平臺的文件系統。
指明該文件系統所使用的【文件分隔符、路徑分隔符、路徑標準化、路徑解析、文件訪問安全控制、文件元信息訪問、文件操做等】。
文件分割符是指路徑名中分割目錄的符號,linux = '/', windows = '\' ;
路徑分割符是環境變量中分割多個路徑的符號,linux = ':' , windows = ';' ;
UnixFileSystem 提供了 FileSystem 的一個實現示例。
存在 BA_EXISTS = 0x01;
普通文件 BA_REGULAR = 0x02;
目錄 BA_DIRECTORY = 0x04;
隱藏 BA_HIDDEN = 0x08;
可讀 ACCESS_READ = 0x04;
可寫 ACCESS_WRITE = 0x02;
可執行 ACCESS_EXECUTE = 0x01;
判斷文件屬性及訪問權限採用與或非位運算實現, 優勢是能夠靈活組合。
序列化是實現內存數據持久化的機制。能夠將 Java 對象數據存儲到文件中,或者將文件中的數據恢復成Java對象。Java RPC, Dubbo,ORM 等都依賴於序列化機制。 序列化相關的類及方法: Serializable/Externalizable, ObjectInputStream, ObjectOutputStream, readObject, writeObject 。 使用 ObjectInputStream.readObject, ObjectOutputStream.writeObject 方法進行對象序列化:
JDK提供的標準對象序列化方式;對象必須實現 Serializable 接口;
適用於任何 Java 對象圖的序列化,通用的序列化方案;輸出爲可讀性差的二進制文件格式,很難爲人所讀懂,沒法進行編輯;
對某些依賴於底層實現的組件,存在版本兼容和可移植性問題;只有基於 Java 的應用程序能夠訪問序列化數據; 使用 XMLDecoder.readObject, XMLEncoder.writeObject 方法進行對象序列化:
對象是普通的 javaBean,無需實現 Serializable 接口;輸出爲可讀性良好的XML文件,容易編輯和二次處理;
其它平臺亦可訪問序列化數據;能夠修改數據成員的內部結構的實現,只要保留原有bean屬性及setter/getter簽名,就不影響序列化和反序列化;
能夠解決版本兼容的問題,提供長期持久化的能力;每一個須要持久化的數據成員都必須有一個 Java bean 屬性。 實現 Externalizable 接口來進行對象序列化。
需在對象中實現 readObject, writeObject 方法,提供更高的序列化靈活性,由開發者自行決定實例化哪些字段、何種順序、輸出格式等細節。
java.nio 爲 javaIO 體系引入了更好的性能改善,主要是 Buffer, Channel 和 file 相關的支撐類。
因爲 java.io 類已經引用了 nio 裏的類,所以直接使用 java.io 裏的類就能夠得到 nio 帶來的好處了。
首先咱們看一個例子:windows
字節流讀寫測試,能夠發現若是預先規定了Byte對象的大小,就會產生亂碼:數組
1 package com.io.test; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.OutputStream; 10 11 public class ByteTest { 12 13 public static void main(String[] args) { 14 15 // 第一種方式讀文件,由於方法throws了異常,因此在這要捕獲異常 16 try { 17 ByteTest.readFromFileByByte(); 18 } catch (FileNotFoundException e) { 19 e.printStackTrace(); 20 System.out.println("找不到文件啊"); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 System.out.println("讀不成功啊!"); 24 } 25 26 System.out.println("==========================="); 27 28 // 第二種方式讀文件 29 try { 30 ByteTest.readFromFileByteTwo(); 31 } catch (IOException e) { 32 e.printStackTrace(); 33 System.out.println("仍是讀不成功啊!"); 34 } 35 System.out.println("==========================="); 36 ByteTest.WriteToFile(); 37 } 38 39 /** 40 * 第一種方法讀文件 經過字節流讀取文件中的數據 41 * 42 * @throws IOException 43 */ 44 public static void readFromFileByByte() throws IOException { 45 File file = new File("abc.txt"); 46 // 若是文件不存在則建立文件 47 if (!file.exists()) { 48 file.createNewFile(); 49 } 50 InputStream inputStream = new FileInputStream(file); 51 // 這裏定義了數組的長度是1024個字節,若是文件超出這字節,就會溢出,結果就是讀不到1024字節之後的東西 52 byte[] bs = new byte[2048]; 53 // 這裏len得到的是文件中內容的長度 54 int len = inputStream.read(bs); 55 inputStream.close(); 56 System.out.println(len+new String(bs)); 57 } 58 59 /** 60 * 第二種方法讀文件 經過字節流讀取文件中的數據 61 * 62 * @throws IOException 63 */ 64 public static void readFromFileByteTwo() throws IOException { 65 // 注意這裏的不一樣,File.separator是分隔符,這裏指明絕對路徑,即D盤根目錄下的abc.txt文件 66 File file = new File("d:" + File.separator + "abc.txt"); 67 // 若是文件不存在則建立文件 68 if (!file.exists()) { 69 file.createNewFile(); 70 } 71 InputStream inputStream = new FileInputStream(file); 72 // 這裏也有不一樣,能夠根據文件的大小來聲明byte數組的大小,確保能把文件讀完 73 byte[] bs = new byte[(int) file.length()]; 74 // read()方法每次只能讀一個byte的內容 75 inputStream.read(bs); 76 inputStream.close(); 77 System.out.println(new String(bs)); 78 } 79 80 public static void WriteToFile() { 81 File file = new File("D:" + File.separator + "write.txt"); 82 OutputStream outputStream = null; 83 if (!file.exists()) { 84 try { 85 // 若是文件找不到,就new一個 86 file.createNewFile(); 87 } catch (IOException e) { 88 e.printStackTrace(); 89 } 90 } 91 try { 92 // 定義輸出流,寫入文件的流 93 outputStream = new FileOutputStream(file); 94 } catch (FileNotFoundException e) { 95 e.printStackTrace(); 96 } 97 // 定義將要寫入文件的數據 98 String string = "Hell Java, Hello World, 你好,世界!"; 99 // 把string轉換成byte型的,並存放在數組中 100 byte[] bs = string.getBytes(); 101 try { 102 // 寫入bs中的數據到file中 103 outputStream.write(bs); 104 outputStream.close(); 105 } catch (IOException e) { 106 e.printStackTrace(); 107 } 108 109 // =================到此,文件的寫入已經完成了! 110 // 若是想在文件後面追加內容的話,用下面的方法 111 OutputStream outToFileEnd = null; 112 try { 113 outToFileEnd = new FileOutputStream(file, true); 114 String string2 = "Here I come!!"; 115 byte[] bs2 = string2.getBytes(); 116 outToFileEnd.write(bs2); 117 outToFileEnd.close(); 118 } catch (FileNotFoundException e) { 119 e.printStackTrace(); 120 } catch (IOException ex) { 121 ex.printStackTrace(); 122 } 123 } 124 }
字符流的讀寫,一樣的對於Byte對象的大小設定很是重要,同時寫入的時候並無識別出換行符:緩存
1 package com.io.test; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.File; 6 import java.io.FileNotFoundException; 7 import java.io.FileReader; 8 import java.io.FileWriter; 9 import java.io.IOException; 10 import java.io.Reader; 11 import java.io.Writer; 12 13 public class CharTest { 14 15 public static void main(String[] args) throws IOException { 16 17 File file1 = new File("D:" + File.separator + "test1.txt"); 18 File file2 = new File("D:" + File.separator + "test2.txt"); 19 Writer writer = new FileWriter(file1); 20 Writer writer1 = new FileWriter(file2, true); 21 String string = "今天是教師節!"; 22 writer.write(string); 23 String string2 = "祝願全部的老師教師節快樂!"; 24 writer1.write(string); 25 writer1.write(string2); 26 // 在這必定要記得關閉流 27 writer.close(); 28 writer1.close(); 29 30 ReadFromFile(); 31 ReadFromFile2(); 32 } 33 34 public static void ReadFromFile() throws IOException { 35 File file = new File("d:" + File.separator + "test1.txt"); 36 Reader reader = new FileReader(file); 37 char[] cs = new char[1024]; 38 // 上面定義了一個大小爲1024的char型數組,若是文件內容過大,程序就會報錯,而不是隻讀到1024的大小 39 reader.read(cs, 0, (int) file.length()); 40 System.out.println(cs); 41 reader.close(); 42 } 43 44 public static void ReadFromFile2() throws IOException { 45 try { 46 // 聲明一個可變長的stringBuffer對象 47 StringBuffer sb = new StringBuffer(""); 48 49 Reader reader = new FileReader("d:" + File.separator + "test1.txt"); 50 // 這裏咱們用到了字符操做的BufferedReader類 51 BufferedReader bufferedReader = new BufferedReader(reader); 52 String string = null; 53 // 按行讀取,結束的判斷是是否爲null,按字節或者字符讀取時結束的標誌是-1 54 while ((string = bufferedReader.readLine()) != null) { 55 // 這裏咱們用到了StringBuffer的append方法,這個比string的「+」要高效 56 sb.append(string + "/n"); 57 System.out.println(string); 58 } 59 // 注意這兩個關閉的順序 60 bufferedReader.close(); 61 reader.close(); 62 63 /* 64 * 完整寫入文件 65 */ 66 Writer writer = new FileWriter("d:" + File.separator + "test3.txt"); 67 BufferedWriter bw = new BufferedWriter(writer); 68 // 注意這裏調用了toString方法 69 bw.write(sb.toString()); 70 // 注意這兩個關閉的順序 71 bw.close(); 72 writer.close(); 73 74 } catch (FileNotFoundException e) { 75 e.printStackTrace(); 76 } catch (IOException e) { 77 e.printStackTrace(); 78 } 79 } 80 81 }
在整個io包中,惟一表示與文件自己有關的類就是File類。使用File類能夠進行建立或刪除文件以及文件夾等經常使用操做。安全
要想使用File類,則首先要觀察File類的構造方法,此類的經常使用構造方法以下所示:網絡
1 public File(String pathname): 2 實例化File類的時候,必須設置好路徑。直接根據路徑找到文件。
File類中的主要方法和常量:多線程
一、public static final String pathSeparator 常量 表示路徑的分隔符(windows是:";") 二、public static final String separator 常量 表示路徑的分隔符(windows是:"\") 三、public File(String pathname) 構造方法 建立File類對象,傳入完整路徑 四、public boolean createNewFile() throws IOException 普通方法 建立新文件 五、public boolean delete() 普通方法 刪除文件 六、public boolean exists() 普通方法 判斷文件是否存在 七、public boolean isDirectory() 判斷給定的路徑是不是一個目錄 八、public long length() 普通方法 返回文件的大小 九、public String[] list() 普通方法 列出指定目錄的所有內容,只是列出了名稱 十、public File[] listFiles() 普通方法 列出指定目錄的所有內容,會列出路徑 十一、public boolean mkdir() 普通方法 建立一個目錄 十二、public boolean removeTo(File dest)爲已有的文件重命名
該對象並非流體系中的一員,其封裝了字節流,同時還封裝了一個緩衝區(字符數組),經過內部的指針來操做字符數組中的數據。該對象特色:併發
1、 該對象只能操做文件,因此構造函數接收兩種類型的參數:a.字符串文件路徑;b.File對象。
二、 該對象既能夠對文件進行讀操做,也能進行寫操做,在進行對象實例化時可指定操做模式(r,rw)
注意:該對象在實例化時,若是要操做的文件不存在,會自動建立;若是文件存在,寫數據未指定位置,會從頭開始寫,即覆蓋原有的內容。能夠用於多線程下載或多個線程同時寫數據到文件。
此類的實例支持對隨機訪問文件的讀取和寫入。隨機訪問文件的行爲相似存儲在文件系統中的一個大型 byte 數組。存在指向該隱含數組的光標或索引,稱爲文件指針;輸入操做從文件指針開始讀取字節,並隨着對字節的讀取而前移此文件指針。若是隨機訪問文件以讀取/寫入模式建立,則輸出操做也可用;輸出操做從文件指針開始寫入字節,並隨着對字節的寫入而前移此文件指針。寫入隱含數組的當前末尾以後的輸出操做致使該數組擴展。該文件指針(實現數組隨機讀寫)能夠經過 getFilePointer 方法讀取,並經過 seek 方法設置。一般,若是此類中的全部讀取例程在讀取所需數量的字節以前已到達文件末尾,則拋出 EOFException(是一種 IOException)。若是因爲某些緣由沒法讀取任何字節,而不是在讀取所需數量的字節以前已到達文件末尾,則拋出 IOException,而不是 EOFException。須要特別指出的是,若是流已被關閉,則可能拋出 IOException。
RandomAccessFile類包含了一個記錄指針,用以標識當前讀寫處的位置,當程序新建立一個RandomAccessFile對象時,該對象的文件記錄指針位於文件頭(也就是0處),當讀/寫了n個字節後,文件記錄指針將會向後移動n個字節。除此以外,RandomAccessFile能夠自由的移動記錄指針,便可以向前移動,也能夠向後移動。RandomAccessFile包含了如下兩個方法來操做文件的記錄指針.
1 long getFilePointer(); 返回文件記錄指針的當前位置 2 void seek(long pos); 將文件記錄指針定位到pos位置
RandomAccessFile便可以讀文件,也能夠寫,因此它即包含了徹底相似於InputStream的3個read()方法,其用法和InputStream的3個read()方法徹底同樣;也包含了徹底相似於OutputStream的3個write()方法,其用法和OutputStream的3個Writer()方法徹底同樣。除此以外,RandomAccessFile還包含了一系類的readXXX()和writeXXX()方法來完成輸入和輸出。
RandomAccessFile有兩個構造器,其實這兩個構造器基本相同,只是指定文件的形式不一樣而已,一個使用String參數來指定文件名,一個使用File參數來指定文件自己。除此以外,建立RandomAccessFile對象還須要指定一個mode參數。該參數指定RandomAccessFile的訪問模式,有如下4個值:
1 「r」 以只讀方式來打開指定文件夾。若是試圖對該RandomAccessFile執行寫入方法,都將拋出IOException異常。 2 「rw」 以讀,寫方式打開指定文件。若是該文件尚不存在,則試圖建立該文件。 3 「rws」 以讀,寫方式打開指定文件。相對於」rw」 模式,還要求對文件內容或元數據的每一個更新都同步寫入到底層設備。 4 「rwd」 以讀,寫方式打開指定文件。相對於」rw」 模式,還要求對文件內容每一個更新都同步寫入到底層設備。
Java中I/O操做主要是指使用Java進行輸入,輸出操做,Java全部的I/O機制都是基於數據流進行輸入輸出,這些數據流表示了字符或者字節數據的流動序列。
數據流是一串接二連三的數據的集合,就象水管裏的水流,在水管的一端一點一點地供水,而在水管的另外一端看到的是一股接二連三的水流。數據寫入程序能夠是一段、一段地向數據流管道中寫入數據,這些數據段會按前後順序造成一個長的數據流。對數據讀取程序來講,看不到數據流在寫入時的分段狀況,每次能夠讀取其中的任意長度的數據,但只能先讀取前面的數據後,再讀取後面的數據(不能隨機讀取)。無論寫入時是將數據分屢次寫入,仍是做爲一個總體一次寫入,讀取時的效果都是徹底同樣的。流序列中的數據既能夠是未經加工的原始二進制數據,也能夠是經必定編碼處理後符合某種格式規定的特定數據。所以Java中的流分爲兩種: 1) 字節流:數據流中最小的數據單元是字節 2) 字符流:數據流中最小的數據單元是字符, Java中的字符是Unicode編碼,一個字符佔用兩個字節。
簡而言之:數據流是一組有序,有起點和終點的字節的數據序列。包括輸入流和輸出流。
當程序須要讀取數據的時候,就會創建一個通向數據源的鏈接,這個數據源能夠是文件,內存,或是網絡鏈接。相似的,當程序須要寫入數據的時候,就會創建一個通向目的地的鏈接。
Java.io包中最重要的就是5個類和一個接口。5個類指的是File、OutputStream、InputStream、Writer、Reader;一個接口指的是Serializable。掌握了這些就掌握了Java I/O的精髓了。
Java I/O主要包括以下3層次:
流式部分——最主要的部分。如:OutputStream、InputStream、Writer、Reader等
非流式部分——如:File類、RandomAccessFile類和FileDescriptor等類
其餘——文件讀取部分的與安全相關的類,如:SerializablePermission類,以及與本地操做系統相關的文件系統的類,如:FileSystem類和Win32FileSystem類和WinNTFileSystem類。
I/O流:java.io包裏有4個基本類:InputStream、OutputStream及Reader、Writer類,它們分別處理字節流和字符流。其餘各類各樣的流都是由這4個派生出來的。
1 按來源/去向分類: 2 File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter 3 byte[]:ByteArrayInputStream, ByteArrayOutputStream 4 Char[]: CharArrayReader, CharArrayWriter 5 String: StringBufferInputStream, StringReader, StringWriter 6 7 網絡數據流:InputStream, OutputStream, Reader, Writer 8 InputStream:InputStream 爲字節輸入流,它自己爲一個抽象類,必須依靠其子類實現各類功能,此抽象類是表示字節輸入流的全部類的超類。 繼承自InputStream 的流都是向程序中輸入數據的,且數據單位爲字節(8bit); 9 InputStream是輸入字節數據用的類,因此InputStream類提供了3種重載的read方法,Inputstream類中的經常使用方法: 10 public abstract int read( ):讀取一個byte的數據,返回值是高位補0的int類型值。若返回值=-1說明沒有讀取到任何字節讀取工做結束。 11 public int read(byte b[ ]):讀取b.length個字節的數據放到b數組中。返回值是讀取的字節數。該方法其實是調用下一個方法實現的 12 public int read(byte b[ ], int off, int len):從輸入流中最多讀取len個字節的數據,存放到偏移量爲off的b數組中。 13 public int available( ):返回輸入流中能夠讀取的字節數。注意:若輸入阻塞,當前線程將被掛起,若是InputStream對象調用這個方法的話,它只會返回0,這個方法必須由繼承InputStream類的子類對象調用纔有用。 14 public long skip(long n):忽略輸入流中的n個字節,返回值是實際忽略的字節數, 跳過一些字節來讀取。 15 public int close( ) :使用完後,必須對咱們打開的流進行關閉。 16 17 來看看幾種不一樣的InputStream: 18 FileInputStream把一個文件做爲InputStream,實現對文件的讀取操做 19 ByteArrayInputStream:把內存中的一個緩衝區做爲InputStream使用 20 StringBufferInputStream:把一個String對象做爲InputStream 21 PipedInputStream:實現了pipe的概念,主要在線程中使用 22 SequenceInputStream:把多個InputStream合併爲一個InputStream 23 24 OutputStream 25 OutputStream提供了3個write方法來作數據的輸出,這個是和InputStream是相對應的。 26 public void write(byte b[ ]):將參數b中的字節寫到輸出流。 27 public void write(byte b[ ], int off, int len) :將參數b的從偏移量off開始的len個字節寫到輸出流。 28 public abstract void write(int b) :先將int轉換爲byte類型,把低字節寫入到輸出流中。 29 public void flush( ) : 將數據緩衝區中數據所有輸出,並清空緩衝區。 30 public void close( ) : 關閉輸出流並釋放與流相關的系統資源。 31 32 幾種不一樣的OutputStream: 33 ByteArrayOutputStream:把信息存入內存中的一個緩衝區中 34 FileOutputStream:把信息存入文件中 35 PipedOutputStream:實現了pipe的概念,主要在線程中使用 36 SequenceOutputStream:把多個OutStream合併爲一個OutStream 37 Reader和InputStream相似;Writer和OutputStream相似。 38 有兩個須要注意的: 39 InputStreamReader : 從輸入流讀取字節,在將它們轉換成字符。 40 BufferedReader :接受Reader對象做爲參數,並對其添加字符緩衝器,使用readline()方法能夠讀取一行。 41 42 如何選擇I/O流 43 肯定是輸入仍是輸出 44 輸入:輸入流 InputStream,Reader 45 輸出:輸出流 OutputStream,Writer 46 47 明確操做的數據對象是不是純文本 48 是:字符流 Reader,Writer 49 否:字節流 InputStream,OutputStream 50 51 明確具體的設備。 52 文件: 53 讀:FileInputStream,, FileReader, 54 寫:FileOutputStream,FileWriter 55 數組: 56 byte[ ]:ByteArrayInputStream, ByteArrayOutputStream 57 char[ ]:CharArrayReader, CharArrayWriter 58 59 String: 60 StringBufferInputStream(已過期,由於其只能用於String的每一個字符都是8位的字符串), StringReader, StringWriter 61 Socket流 62 鍵盤:用System.in(是一個InputStream對象)讀取,用System.out(是一個OutputStream對象)打印 63 是否須要轉換流 64 是,就使用轉換流,從Stream轉化爲Reader、Writer:InputStreamReader,OutputStreamWriter 65 是否須要緩衝提升效率 66 是就加上Buffered:BufferedInputStream, BufferedOuputStream, BufferedReader, BufferedWriter
是否須要格式化輸出
下面再看一個例子:
1 package com.io.test; 2 3 import java.io.BufferedReader; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.FileReader; 8 import java.io.FileWriter; 9 import java.io.IOException; 10 import java.io.InputStreamReader; 11 import java.util.Scanner; 12 13 public class IOTest { 14 15 public static void main(String[] args) throws Exception{ 16 System.out.println("將標準輸入(鍵盤輸入)顯示到標準輸出(顯示器),支持字符:"); 17 inputAndOutput(); 18 19 System.out.println("將IOTest.java的內容打印到顯示器1:"); 20 inputFileAndShow1(); 21 22 System.out.println("將IOTest.java的內容打印到顯示器2:"); 23 inputFileAndShow2(); 24 25 System.out.println("將文件A的內容拷貝到文件B:"); 26 copyAToB(); 27 28 System.out.println("將標準輸入的內容寫入文件:"); 29 systemToFile(); 30 } 31 32 33 //將標準輸入(鍵盤輸入)顯示到標準輸出(顯示器),支持字符。 34 public static void inputAndOutput(){ 35 char ch; 36 BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); //將字節流轉爲字符流,帶緩衝 37 try { 38 while ((ch = (char) in.read()) != -1 && ch != 'q'){ 39 System.out.print(ch); 40 } 41 } catch (IOException e) { 42 e.printStackTrace(); 43 } 44 } 45 46 47 //將IOTest.java的內容打印到顯示器 48 public static void inputFileAndShow1() throws IOException{ 49 BufferedReader in = new BufferedReader(new FileReader("IOTest.java")); 50 String s; 51 try { 52 while ((s = in.readLine()) != null){ 53 System.out.println(s); 54 } 55 in.close(); 56 } catch (IOException e) { 57 e.printStackTrace(); 58 } 59 } 60 public static void inputFileAndShow2() throws FileNotFoundException{ 61 FileReader in = new FileReader("IOTest.java"); 62 int b; 63 try { 64 while ((b = in.read()) != -1){ 65 System.out.print((char)b); 66 } 67 in.close(); 68 } catch (IOException e) { 69 e.printStackTrace(); 70 } 71 } 72 73 74 //將文件A的內容拷貝到文件B 75 public static void copyAToB() throws IOException{ 76 FileInputStream in = new FileInputStream("IOTest.java"); 77 FileOutputStream out = new FileOutputStream("copy.txt"); 78 int b; 79 while ((b = in.read()) != -1){ 80 out.write(b); 81 } 82 out.flush(); 83 in.close(); 84 out.close(); 85 } 86 87 88 //將標準輸入的內容寫入文件 89 public static void systemToFile() throws IOException{ 90 Scanner in = new Scanner(System.in); 91 FileWriter out = new FileWriter("systemIn.log"); 92 String s; 93 while (!(s = in.nextLine()).equals("Q")){ 94 out.write(s + "\n"); 95 } 96 out.flush(); 97 out.close(); 98 in.close(); 99 } 100 101 }
對於文件的操做,本質上就是對內存數據的讀寫而且解析和表示的問題,必不可少的會牽扯到管道,緩存等機制,在java的IO之中使用了裝飾器模式很是的優美和簡潔,值得深刻思考。
參考文獻: http://www.javashuo.com/article/p-hroebtqt-bo.html
http://www.cnblogs.com/lovesqcc/p/5201929.html
https://blog.csdn.net/Named_BaoYong/article/details/74626789