從本文開始,將正式進入JavaIO的簡介
在繼續javaIO系列的文章之前
可以過去看一下 本人博客上的設計模式中的 適配器模式和裝飾器模式
這會對接下來的閱讀大有幫助
本文是從邏輯上介紹整個的Java IO類家譜畫像.
1.流
計算機以及互聯網的世界發展不過短短幾十年,但是這幾十年卻是日新月異
系統的復雜度也越來越高,程序設計語言的抽象程度也越來越高
但是無論如何都繞不開一個話題,那就是IO
之前已經介紹過,IO 就是輸入 和 輸出, 入和出是相對於應用程序來說的
而且,經常一個程序的輸出可能是另一個程序的輸入
這本身就是一個抽象的概念
並沒有"必須怎么樣,那才叫IO"的說法
從數據庫,從文件,從內存,從網絡拿數據,你都可以叫做輸入,數據寫出,都可以叫做輸出,這並沒有什么好糾結的
在java中使用流這一概念來描述輸入和輸出
流的字面含義
百度百科中是這樣描述流的, 可以看得出來 , 流本身就包含了 這樣一層含義
物質 一個地方 流向了 另一個地方 |
在繼續之前,我們先回想下放暑假或者開學時候的場景
假定你需要做汽車和火車,如下圖所示
上圖中有幾個關鍵概念
主體 | 人 人從一個地方到了另一個地方 |
源/目的 | 學校/家 |
方向 | 回家或者返校 圖中的兩個箭頭 |
中間形式 | 火車和汽車 |
1.1流到底是什么
我們再舉一個比較簡單的例子, 使用 水管 往桶里面加水或者抽水
1.2程序語言中的流的主要概念
含義/源/方向/數據形式/中間形式 |
流的含義:
在程序設計中,流是對於數據流動傳輸的一種抽象描述
任何有能力產出數據的數據源,或者有能力接受數據的接收端對象都是一個流 (也就是上面例子中的一個容器接上水管)
|
流的源和目的:
數據可能從本地文件讀取,或者寫入, 也可能發送到網絡上,這就是源和目的
|
流的方向:
同水管里面的流水一樣,也是只有兩個方向,流進或者流出,也就是我們常說的輸入 和 輸出
|
流的數據形式: 數據的具體形式就是流傳送的內容,可能是字節,也能是字符,這就是數據的形式 |
流的中間形式: 對於任何一個流對它的功能進行一些必要的擴充,就好像接上了轉接頭的流可以接到其他規格的水管一樣 在一個流的基礎上 包裝,裝飾上其他的一些功能,流就會變得更加強大 |
1.3 流相關概念詳細解讀
1.3.1 流的源和目的
1.文件
最基本的一個數據源就是我們前文提到過的文件,文件不僅java中有,其他語言中也擁有文件的概念
|
2.字節數組
數據最基本的單位是字節
數組是在程序設計中,為了處理方便, 把具有相同類型的若干變量按有序的形式組織起來的一種形式
這些按序排列的同類數據元素的集合稱為數組
所以字節數組,自然是為了更方便操作字節的一種數據組織形式
|
3. 字符數組/String對象
既然數組可以簡化更方便的進行操作,而且也有字節數組
是不是還應該有字符數組呢?
而且,java中的String對象 ,它的內部實現也是char數組,java中使用char表示字符,這不就是字符數組么
|
4. 管道
"管道"的概念也是類似字面含義,一端輸入,就可以從一端流出,就好像一個水管一樣,
主要用來多線程之間直接進行數據交互,所以說數據來源也可能是一個管道
|
5.網絡等
其他數據源比如網絡等,java的強項就是WEB,從網絡接收數據是再自然不過的事情
|
6.流 另外流本身也可以作為一種源,所以一個流的源可以來自另外的一個流 |
1.3.2 流的方向
流的方向很簡單,只有兩個方向,輸入 或者 輸出
|
1.3.3 流的數據形式
計算機存儲數據是二進制的 0 1 序列
計算機中存儲容量的最小的單位是位(bit)最基本的單位是
字節
(byte)
字節是通過網絡傳輸信息(或在硬盤或內存中存儲信息)的單位
也就是說任何其他形式的數據,都可以而且,最終也都是用字節來表示
所以數據最基本的形式就是字節
1 byte = 8 bit
|
我們的世界充滿了各種符號
字符是表示數據和信息的字母、數字或其他符號
在電子計算機中,每一個字符與一個二進制編碼相對應,這是一個編碼的過程
|
所以說,數據的基本形式有 字節 和 字符兩種形式 |
1.3.4流的中間形式
放學回家的例子,我們很清楚的知道,火車和汽車是我們 人的中間形式過程,經過轉換(買票上車), 地上的人看不到我們了,看到的只是火車 對於流來說,中間形式是什么樣子的呢? 比如我們想要把一個Int類型直接寫入到文件中,怎么辦呢? 我們是不是需要把這個類型的數據處理下 轉換下呢 或者說包裝下 就如同你坐上了車(車把你裝了進去,形式就是車),總之就是要處理下 比如想要緩沖,按照行,按照字等等 這就是一種中間形式,后面我們會詳細介紹涉及到的中間形式 |
不過很顯然,中間形式並沒有向從某種數據源讀取數據那么剛需 但是他會給你提供更多的功能,讓你的流功能更加多變,擴展 如果有了中間形式,你可能就能夠直接把一個int寫入到文件上,這不是很方便么 |
1.3.5流的種類-基本功能 擴展功能
想要完成一個IO類庫的基本功能,只需要把握住三點 |
1. 流的源和目的
2. 流的數據形式
3. 流的方向
|
想要做得更好就需要把握好流的中間形式,提供更強大的功能 |
流的源和目的 | 文件 / 字節數組 /管道 /字符數組/String對象 / 網絡 / 流 |
流的數據形式 | 字符 / 字節 |
流的方向 | 輸入 / 輸出 |
現在我們掌握了流的基本屬性,上表中的三種,也掌握了他們可能的變量值
很簡單,只需要使用簡單的組合進行計算,我們就可以列舉出來所有可能的組合
下面我們試着列一些(並不會列出來全部內容)
文件(源) | 輸入 | 字節 |
文件(源) | 輸入 | 字符 |
文件(目的地) | 輸出 | 字節 |
文件(目的地) | 輸出 | 字符 |
字節數組(源) | 輸入 | 字節 |
字節數組(源) | 輸入 | 字符 |
字節數組(目的地) | 輸出 | 字節 |
字節數組(目的地) | 輸出 | 字符 |
管道(源) | 輸入 | 字節 |
管道(源) | 輸入 | 字符 |
管道(目的地) | 輸出 | 字節 |
管道(目的地) | 輸出 | 字符 |
等等................等等................
還有很多種組合, 我相信你肯定可以排列的出來
不過很顯然,我們此處只是簡單的羅列,窮舉出所有組合的可能
對於類庫的設計自然不能這么簡單暴力,或許有些組合沒有必要,或許有些組合不符合邏輯
去掉那些無用的,不合邏輯的,無意義的,那么剩下來的組合形式,其實就是IO類庫要解決的問題
也就是就剩下了我們現在看得到的JavaIO類庫了 接下來從整體上對IO類庫進行介紹
2. JAVA IO類庫體系結構
java.io
java.io包中(JDK8),有87個類,其中有一些輔助類 還有一些異常類
去掉這些之后,剩下的絕大多數都是IO類體系的直接相關類,看起來很雜亂繁多
我們接下來講從整體上對涉及到的IO類進行介紹,等看完本篇文章,相信你應該能有一個整體的把控
只有從整體把控才有可能掌握整個完整的類家族
2.1 流的四大家族
如果先不考慮數據的來源,根據流的方向(輸入 和 輸出)以及流的數據形式(字符 和 字節) 我們有四種形式
輸入 | 字節 |
輸出 | 字節 |
輸入 | 字符 |
輸出 | 字符 |
四種形式 | 輸入字節 | 輸出字節 | 輸入字符 | 輸出字符 |
Java中名稱 | InputStream | OutPutStream | Reader | Writer |
可以看得出來在命名上,類庫設計者的一些想法
把字節使用Stream作為后綴,或許因為字節是最基本的單位,所以他才是流Stream
我們平時閱讀 read和書寫write的都是字符,所以使用Reader 和 Writer表示字符的輸入和輸出也很自然
|
節點流與過濾流
我們上面講述流的含義概念時,反復提到了流的基本功能以及中間形式
基本功能就是針對於不同數據源的操作,屬於剛需范圍
而中間形式則是剛需的強有力的增強
流的數據源/目的 流的方向 流的數據類型的組合,構成了基本功能的完整集合
而對於增強型的流的形式,則是Java IO出彩的地方,屬於增強型的功能
java中針對於基本數據源進行操作的流叫做
節點流
而對於那些起到增強裝飾功能的流,叫做
過濾流
按照我們上面的思維邏輯
只需要把相關的數據源與我們上面的這四種基本形式進行組合
就可以得到流的基本功能家族,也就是節點流
根據節點流需要的拓展功能,我們就可以推演出來過濾流
2.2 流體系類層次結構詳解
2.2.1 InputStream
2.2.1.1 InputStream節點流
數據源與InputStream的結合
字節數組 | ByteArrayInputStream (java.io) |
文件 |
FileInputStream (java.io)
|
管道 |
PipedInputStream (java.io)
|
String |
|
對象 | ObjectInputStream (java.io) |
上面就是IO類庫提供給我們的基礎功能
也就是可用的有效的合理的數據源與InputStream的組合(InputStream 流的方向與流的數據形式的組合)
類名 | 功能 | 構造方法 |
ByteArrayInputStream | 從字節數組中讀取數據,也就是從內存中讀取數據
包含一個內部緩沖區,指向該字節數組
內部計數器跟蹤 read 方法要提供的下一個字節
關閉 ByteArrayInputStream 無效
此類中的方法在關閉此流后仍可被調用,而不會產生任何 IOException
|
ByteArrayInputStream(byte buf[]) ByteArrayInputStream(byte buf[], int offset, int length) 不是復制而來,直接指向地址 多參數的帶偏移量 |
FileInputStream | 用於從文件中讀取信息 | FileInputStream(String name) FileInputStream(File file) FileInputStream(FileDescriptor fdObj) 使用文件路徑名 抽象路徑名File 或者文件描述符 |
PipedInputStream | 產生用於寫入相關Pipe的OutputStream的數據 實現管道化的概念 管道輸入流應該連接到管道輸出流; 管道輸入流提供要寫入管道輸出流的所有數據字節 通常,數據由某個線程從 PipedInputStream 對象讀取 並由其他線程將其寫入到相應的 PipedOutputStream 不建議對這兩個對象嘗試使用單個線程,因為這樣可能死鎖線程 |
PipedInputStream(PipedOutputStream src) PipedInputStream(PipedOutputStream src, int pipeSize) PipedInputStream() PipedInputStream(int pipeSize) |
棄用,如果條件允許可以考慮使用StringReader | ||
ObjectInputStream |
對以前使用 ObjectOutputStream 寫入的基本數據和對象進行反序列化
ObjectOutputStream 和 ObjectInputStream 分別與 FileOutputStream 和 FileInputStream 一起使用時
可以為應用程序提供對對象圖形的持久存儲
ObjectInputStream 用於恢復那些以前序列化的對象
其他用途包括使用套接字流在主機之間傳遞對象,或者用於編組和解組遠程通信系統中的實參和形參。
|
ObjectInputStream(InputStream in) ObjectInputStream() |
SequenceInputStream可以說既不是節點流也不是過濾流,硬要算的話,可以說是節點流 算是一個工具類一樣的存在
SequenceInputStream (java.io) | 兩個或者多個InputStream對象轉換為單一的InputStream | SequenceInputStream(InputStream s1, InputStream s2) SequenceInputStream(Enumeration<? extends InputStream> e) |
2.2.1.2 InputStream過濾流
介紹過了InputStream的節點流,我們看下,我們還希望InputStream能夠哪些擴展的功能,也就是上面提到過的 流的中間形式
我們之前就提到過,希望能夠有直接操作數據類型的流,通過這個流可以直接操作基本數據類型的讀寫,而不需要自己去處理字節或者字節數組等
也就是說我們希望能夠對基本數據類型進行支持
|
IO是操作系統的瓶頸,如果過於頻繁的直接對磁盤IO進行讀寫,勢必會增加CPU的空閑,性能降低,我們希望能夠有緩沖的功能
|
IDE開發工具的編輯器都有行號的標志,行號可以給我們提供很多的便捷性,所以希望能夠跟蹤展示行號 |
比如當我們用程序讀取一行代碼,識別其中的關鍵字 比如 int i = 0; 讀取到int時,我們不知道他是不是關鍵字,可能是一個int0的變量名 讀取到下一個的時候,發現是空格,我們才能確定,他就是一個關鍵字 但是下面的空格已經被讀取了,我們可能希望接下來的掃描能夠讀取到空格,可是流是順序的,被消費了就不存在了 所以希望能夠把讀取到的字節回退到原來的流中 |
於是就有了
支持基本數據類型/緩存/行號/回退 這幾種擴展功能的想法
功能點和InputStream組合下可以得到如下四種擴展功能
Data表示基本數據類型 Buffer 表示緩沖 LineNumber表示行號 PushBack表示回退
DataInputStream (java.io)
BufferedInputStream (java.io)
LineNumberInputStream (java.io)
PushbackInputStream (java.io)
|
到底怎么實現呢?
顯然我們可以直接通過實現InputStream來實現這幾個子類,用於表示這幾個功能
但是就又出現了一個問題,如果既想要 支持基本數據類型,又想具有緩沖的功能怎么辦? 如果還用繼承的想法會出現什么問題?
那就又回到了組合的問題上來了,4種功能就會出現4*3*2*1=24 中組合,類的個數直接爆炸了.....
回想下我們之前想到過的設計模式---> 裝飾器模式
就可以完美的解決這個問題,裝飾器模式是繼承的一種良好替代方式,能過有效的避免類的個數的爆炸問題
並且還能夠動態的增加或者減少功能
看下UML圖
通過UML圖可以看得到,我們還需要一個Decorator類,我們的具體的裝飾類個數不止一個,顯然不能省略這個Decorator抽象類
(不清楚裝飾器模式的沒辦法理解這種邏輯,請務必看明白裝飾器模式)
這個Decorator就是我們的FilterInputStream (java.io)
看下類圖,黑色部分為裝飾器模式的角色 節點流表示上面說到的節點流 ByteArrayInputStream/FileInputStream/PipedInputStream/ObjectInputStream/ FilterInputStream中包含一個InputStream屬性(是你還有你) ![]() |
![]() |
下面我們看下InputStream下的類繼承體系
現在你是否已經可以大致的明白,這些類都是做什么的了呢?
另外還有一些不在java.io包中的類
這些不是IO主體系內的東西,但是依賴於IO ,從事着跟IO相關的一些工作,所以也擴展自InputStream
后面將會單獨進行介紹,此處不展開討論
SocketInputStream (java.net)
CheckedInputStream (java.util.zip)
DeflaterInputStream (java.util.zip)
GZIPInputStream (java.util.zip)
InflaterInputStream (java.util.zip)
ZipInputStream (java.util.zip)
JarInputStream (java.util.jar)
|
2.2.2 OutputStream
2.2.2.1 OutputStream節點流
數據源與OutputStream的結合
字節數組 | ByteArrayOutputStream (java.io) |
文件 |
FileOutputStream (java.io)
|
管道 |
PipedOutputStream (java.io)
|
對象 | ObjectOutputStream (java.io) |
仍舊是數據源與OutputStream的組合
ByteArrayOutputStream |
其中的數據被寫入一個 byte 數組
緩沖區會隨着數據的不斷寫入而自動增長, 可使用 toByteArray() 和 toString() 獲取數據
關閉 ByteArrayOutputStream 無效
此類中的方法在關閉此流后仍可被調用,而不會產生任何 IOException
|
ByteArrayOutputStream() ByteArrayOutputStream(int size) 無參會調用有參,設置默認值 |
FileOutputStream | 信息寫入文件 | FileOutputStream(String name) FileOutputStream(String name, boolean append) FileOutputStream(File file) FileOutputStream(File file, boolean append) FileOutputStream(FileDescriptor fdObj) 與FileInputStream幾乎一樣,不同的是第二個參數用於設置是否是append追加 |
PipedOutputStream | 可以將管道輸出流連接到管道輸入流來創建通信管道 管道輸出流是管道的發送端 通常,數據由某個線程寫入 PipedOutputStream 對象 並由其他線程從連接的 PipedInputStream 讀取 不建議對這兩個對象嘗試使用單個線程,因為這樣可能會造成該線程死鎖 |
PipedOutputStream(PipedInputStream snk) PipedOutputStream() |
ObjectOutputStream | ObjectOutputStream 將 Java 對象的基本數據類型和圖形寫入 OutputStream 可以使用 ObjectInputStream 讀取(重構)對象 通過在流中使用文件可以實現對象的持久存儲 如果流是網絡套接字流,則可以在另一台主機上或另一個進程中重構對象 |
ObjectOutputStream(OutputStream out) ObjectOutputStream() |
2.2.2.2 OutputStream過濾流
類似InputStream,OutputStream也需要有支撐基本數據類型的功能,以及緩沖的功能
另外,既然是輸出,還希望能夠輸出各種類型的數據,這樣子將會更加方便
也就是
基本數據類型支持/緩沖/便捷輸出 |
DataOutputStream (java.io)
BufferedOutputStream (java.io)
PrintStream (java.io)
|
同InputStream 一樣
擴展的功能,類庫設計者依然是使用裝飾器模式
FilterOutputStream (java.io) 是我們的Decorator
完整的家譜
非IO包中的,但是卻跟IO相關的一些功能點,跟OutputStream相關的類 |
SocketOutputStream (java.net)
CheckedOutputStream (java.util.zip)
DeflaterOutputStream (java.util.zip)
GZIPOutputStream (java.util.zip)
InflaterOutputStream (java.util.zip)
JarOutputStream (java.util.jar)
ZipOutputStream (java.util.zip)
|
擴展的家譜
2.2.3 Reader
2.2.3.1 Reader節點流
數據源與Reader的結合
字符數組 | CharArrayReader (java.io) |
String | StringReader (java.io) |
文件 |
FileReader (java.io)
|
管道 |
PipedReader (java.io)
|
CharArrayReader | 實現一個可用作字符輸入流的字符緩沖區 | CharArrayReader(char buf[]) CharArrayReader(char buf[], int offset, int length) |
StringReader | 其源為一個字符串的字符流 | StringReader(String s) |
FileReader | 用來讀取字符文件的便捷類 | FileReader(String fileName) FileReader(File file) FileReader(FileDescriptor fd) |
PipedReader | 管道字符輸入流 | PipedReader(PipedWriter src) PipedReader(PipedWriter src, int pipeSize) PipedReader() PipedReader(int pipeSize) |
字節和字符作為數據的存儲單位,自然經常有轉換的需要
InputStreamReader 就是InputStream 轉換為Reader的類
InputStreamReader | 轉換為Reader InputStreamReader 是字節流通向字符流的橋梁 它使用指定的 charset 讀取字節並將其解碼為字符 它使用的字符集可以由名稱指定或顯式給定,或者可以接受平台默認的字符集 每次調用 InputStreamReader 中的一個 read() 方法都會導致從底層輸入流讀取一個或多個字節 為了達到最高效率,可要考慮在 BufferedReader 內包裝 InputStreamReader |
InputStreamReader(InputStream in) InputStreamReader(InputStream in, String charsetName) InputStreamReader(InputStream in, Charset cs) InputStreamReader(InputStream in, CharsetDecoder dec) 構造方法很清晰,接受一個InputStream 並且可以自定義字符編碼 |
對於類的轉換,設計模式中使用了適配器模式
通過構造方法接收InputStream,然后通過內部的StreamDecoder處理
StreamDecoder 和 StreamEncoder 是作為字符輸入和輸出轉換的關鍵類,后續有時間會介紹到
屬於適配器模式中的對象適配器模式
Reader 是Target
InputStream 是 被適配者 Adaptee
InputStreamReader 是 Adapter
|
需要注意的是,FileReader 與字節流中的FileInputStream 和 FileOutputStream 也是不一樣的
FileReader 繼承 InputStreamReader
2.2.3.2 Reader過濾流
字符流Reader也依然有裝飾器模式的應用
BufferedReader (java.io)
LineNumberReader (java.io)
PushbackReader (java.io)
|
不過需要注意,Reader字符流的裝飾器模式應用跟字節流的有些差別
在字節流中,擴展功能都是通過FilterInputStream 或者 FilterOutputStream
然而,在我們的Reader中
BufferedReader 和 FilterReader 各自是一個裝飾器模式
Reader家族完整的族譜
2.2.4 Writer
數據源與writer的結合
字符數組 | CharArrayWriter (java.io) |
String | StringWriter (java.io) |
文件 |
FileWriter (java.io)
|
管道 |
PipedWriter (java.io)
|
CharArrayWriter |
實現一個可用作 Writer 的字符緩沖區
緩沖區會隨向流中寫入數據而自動增長 可使用 toCharArray() 和 toString() 獲取數據。
在此類上調用 close() 無效
並且在關閉該流后可以調用此類中的各個方法,而不會產生任何 IOException
|
CharArrayWriter() CharArrayWriter(int initialSize) 內部包含char buf[] size為大小 構造方法用來初始化緩沖區 |
StringWriter | 將輸出收集到一個字符緩沖區 StringBuffer的字符流,可以用來構造字符串 關閉 StringWriter 無效 此類中的方法在關閉該流后仍可被調用,而不會產生任何 IOException |
StringWriter() StringWriter(int initialSize) 構造方法初始化緩沖區 |
FileWriter | 用來寫入字符文件的便捷類 類似FileReader繼承自InputStreamReader 他繼承自OutputStreamWriter |
FileWriter(String fileName) FileWriter(String fileName, boolean append) FileWriter(File file) FileWriter(File file, boolean append) FileWriter(FileDescriptor fd) 構造方法都是用來設置文件 |
PipedWriter | 管道字符流 | PipedWriter(PipedReader snk) PipedWriter() |
轉換流
OutputStreamWriter | 類似InputStreamReader 作為轉換器使用 OutputStreamWriter 是字符流通向字節流的橋梁 可使用指定的 charset 將要寫入流中的字符編碼成字節 使用的字符集可以由名稱指定或顯式給定,否則將接受平台默認的字符集 每次調用 write() 方法都會導致在給定字符(或字符集)上調用編碼轉換器
為了獲得最高效率,可考慮將 OutputStreamWriter 包裝到 BufferedWriter 中
例如:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
|
OutputStreamWriter(OutputStream out, String charsetName) OutputStreamWriter(OutputStream out) OutputStreamWriter(OutputStream out, Charset cs) OutputStreamWriter(OutputStream out, CharsetEncoder enc) 獲取OutputStream然后進行轉換,或者指定具體的字符編碼 |
FilterWriter | 類似其他的Filter類,作為裝飾器模式的Decoder角色 以便具體的裝飾器角色可以使用 |
BufferedWriter 以及 PrintWriter類似Reader 不同於字節流的裝飾器模式應用
他們都自成一個模式的應用
他倆都單獨是Writer 也都包含一個Writer
Writer下完整的家譜
2.3 IO類層次結構總結
前面已經對IO類的基本層次結構進行了一個邏輯上的概述
我們現在歸納概括下一些基本特點
IO的邏輯功能設計點 由 數據源,流的方向,流的數據形式三部分組合而成,這個組合構成了IO的基本功能 另外還有擴展功能,擴展功能以基礎功能作為依托,底層依賴基本功能 每種形式的基本功能和擴展功能構成了該形式的功能的集合 |
數據源形式比較多,但是對於流的數據形式以及流的方向是固定的 所以所有的類的基礎,都是基於 流的數據形式以及流的方向的組合 也就是 字節輸入 字節輸出 字符輸入 字符輸出 這四個形式是固定的 分別使用 InputStream OutputStream Reader Writer來表示這四大家族 前面兩個表示字節 后面兩個表示字符 |
絕大多數的擴展都以 上面四個名詞作為后綴,表示是他的家族成員 |
基本功能對於字節涉及下面幾個關鍵詞 ByteArray File Piped Object
擴展功能對於字節涉及涉及下面幾個關鍵詞
Data Buffered Pushback LineNumber print
|
基本功能對於字符涉及涉及下面幾個關鍵詞
CharArray String File Piped
擴展功能對於字符涉及涉及下面幾個關鍵詞 Buffered Print |
雖然四大家族都由基本功能以及擴展功能組成 但是字符和字節的實現形式卻並不完全相同 字節流的擴展功能比較依賴裝飾器角色FilterInputStream 以及 FilterOutputStream 但是字符流的擴展功能不完全依賴FilterReader 以及 FilterWriter |
數據源與四大家族的結合組合成了基本功能 也就是節點流 擴展功能點與四大家族的結合組成了擴展功能 也就是過濾流 |
另外還有幾個工具一樣的存在 SequenceInputStream 用於合並InputStream InputStreamReader 以及OutputStreamWriter 用於轉換 使用了適配器模式 |
本文主要是從邏輯上介紹了IO家族,雖然實現上都略有差異
但是基本的命名習慣和功能點四個家族是非常類似的
只有理解了類庫的邏輯出發點,才能理解IO整個的類庫,而本文正是從邏輯上去解讀類庫的設計