最基本的字節輸入流,抽象類,定義了讀取原始字節的全部基本方法
1.1.一、public abstract int read() throws IOException 讀取一個字節的方法,最基礎的方法
1.1.二、public int read(byte b[], int off, int len) 讀取指定長度的字節到字節數組,基於方法1.1.1
1.1.三、public int read(byte b[]) throws IOException 讀取一個數組那麼多的字節,基於 方法1.1.2
1.1.四、public long skip(long n) throws IOException 跳過必定字節數,用到的比較少
1.1.五、public int available() throws IOException 返回能夠讀取的最少字節數,用到的比較少
1.1.六、mark(int readlimit)、void reset()和markSupported()這三個方法,並非每一個子類都支持,這裏設計得不合理,徹底能夠把這三個方法遷移到一個新的接口中去。
1.1.七、public void close() throws IOException 關閉輸入流java
最基本的字節輸出流,抽象類,定義了寫入原始字節的全部基本方法
1.2.一、public abstract void write(int b) throws IOException 寫入一個字節,最基礎的方法
1.2.二、public void write(byte b[], int off, int len) throws IOException 將一個字節數組中的部分字節寫入,基於方法1.2.1
1.2.三、public void write(byte b[]) throws IOException 將一個字節數組寫入,基於方法1.2.2
1.2.四、public void close() throws IOException 關閉輸出流
1.2.五、public void flush() throws IOException 刷新輸出流數組
點評:InputStream和OutputStream定義了I/O領域最基礎的行爲,也就是讀取和寫入一個字節,同時使用了模板方法將讀取和寫入的行爲進行了適當擴展。緩存
有了抽象類,就必定會有子類。針對不一樣的數據來源,InputStream和OutputStream存在三種子類:一種是基於內存的ByteArrayInputStream/ByteArrayOutputStream,一種是基於磁盤文件的FileInputStream/FileOutputStream,還有一種是基於網絡的SocketInputStream/SocketOutputStream。
服務器
讀取寫入的源是操做系統的文件
FileInputStream使用native方法進行底層文件的讀取private native int read0() throws IOException;全部其餘的read方法最終都是基於這個本地方法實現。
FileOutputStream使用native方法進行底層文件的寫入private native void writeBytes(byte b[], int off, int len, boolean append)全部其餘的write方法都是基於這個本地方法實現。網絡
讀取寫入的源是內存的一個數組,用的比較少。app
SocketInputStream使用 private native int socketRead0(FileDescriptor fd,byte b[], int off, int len,int timeout)這個native方法讀取遠程服務器的數據流。全部read方法都是基於這個本地方法實現的。
SocketOutputStream 使用private native void socketWrite0(FileDescriptor fd, byte[] b, int off,int len)這個native方法來進行遠程數據流的寫入,全部的write方法都是基於這個方法實現的。框架
點評:InputStream和OutputStream是對流的抽象,不一樣的具體流經過繼承去實現,對於Java本地平臺,最基本的就是基於文件系統的流,當涉及到遠程系統,就會出現網絡流,基於內存的流通常不會用到。socket
裝飾模式能夠對一個類的行爲進行擴展,而且不改變它的接口,Java經過FilterInputStream/FilterOutputStream實現了裝飾模式。
函數
責任鏈模式則是定義統一的接口,而後經過多個實現該接口的子類串行協做完成一項複雜的功能。Java經過將多個FilterInputStream/FilterOutputStream的子類串聯起來實現了責任鏈模式。工具
FilterInputStream自己不實現輸入流的功能,而是經過構造函數傳入另外一個InputStream的子類,把輸入流的功能交給它作。經過繼承FilterInputStream能夠對輸入輸出流的行爲進行擴展,這是裝飾模式的典型用法。經過多個裝飾類實現責任鏈模式,它將對一個輸入流的不一樣處理分散到不一樣的FilterInputStream中去。FilterOutputStream和FilterInputStream的原理同樣。
繼承了FilterInputStream,實現了輸入流處理中的緩衝的功能。底層的流會先被讀取到一個字節數組中,用戶使用BufferedInputStream讀取數據的時候,會先讀取字節數組中的數據,讀完了纔會調用底層的流進行進一步的讀取。這種方法能夠提高讀取的性能。
繼承了FilterOutputStream,實現了輸出流處理中的緩衝功能。當用戶寫入數據的時候,實際上是先寫入到BufferedOutputStream的一個字節數組中,當這個字節數組滿了,纔會真正調用底層的輸出流執行輸出動做。這種方法能夠提高寫入的性能。在使用BufferedOutputStream的寫入功能時,必定要使用flush,由於緩衝數組不滿的時候是不會寫入底層流的,在寫入最後一點數據的時候,緩衝數據不必定被填滿,這時候就須要調用flush進行強制刷新。
繼承FilterOutputStream,這個類的print和println方法能夠把java的一些基本類型數據轉換成字節寫入到底層輸出流,可是PrintStream對String的轉換是平臺相關的,不一樣的平臺會有不一樣的編碼,因此寫入到底層的字節也不一樣,所以PrintStream只適合於測試輸出,不適合於通常的I/O操做,特別是網絡流。
這兩個類繼承了FilterInputStream/FilterOutputStream,用來實現將java基本類型轉換成二進制來進行讀寫操做,這兩個類的readUTF和writeUTF方法使用了一種特殊的UTF編解碼方式,只能用於java程序,所以不建議在網絡流或者跨平臺的應用中使用者兩個類
繼承了FilterInputStream,提供了一種回退的機制,能夠實現unread,本質是使用緩衝數組實現了,也就是說,回退的範圍是有限的。
InputStream和OutputStream是面向字節的,而人類的習慣是面向字符,所以InputStream和OutputStream對於程序猿的用戶體驗不是太好,因而就須要提供一些面向字符的流。因爲DataInputStream/DataOutputStream在跨平臺的狀況下存在問題,所以,java設計者乾脆仿照InputStream和OutputStream從新設計了一套面向字符的I/O,也就是Reader/Writer
基本的字符輸入流,是個抽象類
4.1.一、public abstract int read() throws IOException 讀取一個字符的方法,最基礎的方法
4.1.二、public int read(char b[], int off, int len) 讀取指定長度的字符到字節數組,基於方法4.1.1
4.1.三、public int read(char b[]) throws IOException 讀取一個數組那麼多的字符,基於 方法4.1.2
4.1.四、public long skip(long n) throws IOException 跳過必定字符,用到的比較少
4.1.五、public int available() throws IOException 返回能夠讀取的最少字符,用到的比較少
mark(int readlimit)、void reset()和markSupported()這三個方法,並非每一個子類都支持,這裏設計得不合理,徹底能夠把這三個方法遷移到一個新的接口中去。
4.1.六、public void close() throws IOException 關閉輸入流
4.1.七、public boolean ready() throws IOException 是否已經準備好
基本的字符輸出流,是個抽象類
4.2.一、abstract public void write(char cbuf[], int off, int len) 抽象方法,用於寫入一個字符數組的一部分,須要子類實現
4.2.二、public void write(char cbuf[]) throws IOException 基於4.2.一、,寫入一個字符數據
4.2.三、public void write(int c) throws IOException 將一個int類型的堤16位做爲一個字符寫入,基於4.2.1
4.2.四、public void write(String str) throws IOException 寫入一個字符串,基於4.2.1
4.2.五、public void write(String str, int off, int len) throws IOException 寫入一個字符串的一部分,基於4.2.1
因爲計算機只識別字節,因此Reader/Writer的數據來源最終仍是字節,而他們沒法直接和字節打交道,這時候就須要一箇中介者將Reader/Writer和InputStream和OutputStream進行打通,因而就有了InputStreamReader和OutputStreamWriter
不一樣源的Reader/Writer,他們都繼承InputStreamReader/OutputStreamWriter
繼承了InputStreamReader/OutputStreamWriter,傳入FileInputStream/FileOutputStream做爲底層的字節I/O
繼承了InputStreamReader/OutputStreamWriter,使用char數組做爲數據源,用的比較少
相似於字節流,也使用了裝飾模式和責任鏈模式
對Reader/Writer的代理,底層使用其餘Reader/Writer做爲真正的操做。
繼承了FilterReader/FilterWriter,BufferedReader使用char數組做爲數據的緩衝區,讀取數據先從緩存區讀,讀不到在從底層的Reader讀,Reader其實用到是更底層的InputStream,儘可能不要用BufferedInputStream做爲底層InputStream,兩層緩衝區沒有必要。BufferedWriter先寫入緩衝區,待緩衝區寫滿了再使用底層真正的Writer寫,Writer其實用的是更底層的OutputStream。儘可能不要用BufferedOutputStream做爲底層OutputStream,兩層緩衝區不必。
繼承了FilterReader,實現了可退回的寫,本質是使用了一個char數組,因此可退回是有界限。
用於取代PrintStream,它能夠java基本類型轉換成字節輸出,並且能夠正確處理不一樣字符集的國際化問題。
至此,咱們對java.io包下的相關類都作了詳細的解讀,接下來,讓咱們看看第三方開源框架都對java IO進行了哪些擴展。
經過上面的解讀咱們知道,java IO自己的擴展點有兩個,一個是經過繼承對數據來源的擴展,第二個是經過裝飾模式對行爲進行擴展。下面介紹的commons-io選擇了對行爲進行擴展,並提供一些IO操做的工具方法,簡化IO操做,而okio則不走尋常路,廢棄了java IO的體系,設計出了source/sink接口體系。
8.1.一、擴展行爲
最新的commons-io 2.5提供了對input和output的各類擴展,經過繼承FilterInputStream/FilterOutputStream實現
input:
AutoCloseInputStream:當IO流讀到EOF時,會進行自動關閉
BOMInputStream:用於處理含有BOM的輸入流,好比Windows下用記事本保存的文件
BoundedInputStream:含有讀取界限的輸入流,超過這個界限讀取將會中止
CountingInputStream:含有統計功能的輸入流
DemuxInputStream:這個輸入流會將真正的流保存在ThreadLocal中
ProxyInputStream:一個抽象類,提供了讀取一個字節以前後以後的回調方法
TaggedInputStream:這個類在拋異常的時候會給異常設置標記,從而用於跟蹤異常
TeeInputStream:從一個源讀取數據,同時會保存到一個指定的源,相似於unix的tee命令
UnixLineEndingInputStream:這個流在讀取換行符的時候會按照unix格式讀取
WindowsLineEndingInputStream:這個流在讀取換行符的時候會按照Windows格式讀取
output
ChunkedOutputStream:寫入流的時候按照chunk分批寫入
CountingOutputStream:具備統計功能的輸出流
DemuxOutputStream:這個輸出流會將真正的流保存在ThreadLocal中
ProxyOutputStream:一個抽象類,提供了寫入一個字節以前後以後的回調方法
TaggedOutputStream:這個類在拋異常的時候會給異常設置標記,從而用於跟蹤異常
TeeOutputStream:寫數據到一個源,同時會保存到一個指定的源,相似於unix的tee命令
8.1.二、工具方法
IOUtils工具類,主要提供如下工具方法:
closeQuietly - 關閉一個流,忽略異常
toXxx/read - 從某個流讀取數據
write - 向某個流寫入數據
copy -從一個流複製到另外一個流
contentEquals - 比較兩個流中的內容
若是使用原生的Java IO進行基本類型的讀寫,咱們須要使用DataInputStream/DataOutputStream以及BufferedReader/BufferedWriter這四個類,除此以外,咱們還須要瞭解InputStreamReader/OutputStreamWriter以及Java IO之間的責任鏈,對於通常的Java開發者來講,這顯然太複雜了。因而okio從新設計了接口Source/Sink,提供了訪問基本類型的接口和緩衝功能,同時屏蔽了底層複雜的IO體系,開發者只要傳入InputStream和OutputStream就能夠了。
具體的類關係以下:
使用Okio的Java代碼以下:
try {
BufferedSource bufferedSource = Okio.buffer(Okio.source(new FileInputStream("1.txt"))); int i = bufferedSource.readInt(); long l = bufferedSource.readLong(); String s = bufferedSource.readString(Charset.forName("UTF-8")); BufferedSink bufferedSink = Okio.buffer(Okio.sink(new FileOutputStream("2.txt"))); bufferedSink.writeInt(1); bufferedSink.writeLong(2L); bufferedSink.writeString("123", Charset.forName("UTF-8")); } catch (Exception e) { // process exception }