流分類回顧
本文是JavaIO告一段落的總結帖
但願對JavaIO作一個基礎性的總結(不涉及NIO)
從實現的角度進行簡單的介紹
下面的這兩個表格,以前出現過
數據源形式 |
InputStream |
OutputStream |
Reader |
Writer |
ByteArray(字節數組) |
ByteArrayInputStream |
ByteArrayOutputStream |
無 |
無 |
File(文件) |
FileInputStream |
FileOutputStream |
FileReader |
FileWriter |
Piped(管道) |
PipedInputStream |
PipedOutputStream |
PipedReader |
PipedWriter |
Object(對象) |
ObjectInputStream |
ObjectOutputStream |
無 |
無 |
String |
StringBufferInputStream |
無 |
StringReader |
StringWriter |
CharArray(字符數組) |
無 |
無 |
CharArrayReader |
CharArrayWriter |
擴展功能點 |
InputStream |
OutputStream |
Reader |
Writer |
Data(基本類型) |
DataInputStream |
DataOutputStream |
無 |
無 |
Buffered(緩衝) |
BufferedInputStream |
BufferedOutputStream |
BufferedReader |
BufferedWriter |
LineNumber(行號) |
LineNumberInputStream |
無 |
LineNumberReader |
無 |
Pushback(回退) |
PushbackInputStream |
無 |
PushbackReader |
無 |
Print(打印) |
無 |
PrintStream |
無 |
PrintWriter |
流分爲輸入輸出流兩種形式
數據格式又分爲字節和字符兩種形式
他們組合而來了四你們族
InputStream OutputStream Reader Writer
|
全部的四你們族的流有兩種合成擴展方式: 按照數據源形式擴展 按照裝飾功能點擴展 |
數據源形式擴展
如今咱們換一個維度,從實現的角度,從新介紹下IO
數據源擴展的根本 |
從這種形式的數據中讀取數據 寫入數據到這種數據形式 |
咱們上面列出來了ByteArray File Piped Object String CharArray 這幾種經常使用的數據源形式
結合咱們上面的概念,咱們看一下,實際的實現
字節數組 / 字符數組 /String
ByteArray 內存數據 |
ByteArrayInputStream |
內部有一個byte buf[] 引用 指向實際保存數據的那個字節數組 |
ByteArrayInputStream(byte buf[]) ByteArrayInputStream(byte buf[], int offset, int length) 構造方法將內部的byte buf[] 引用指向某個 byte buf[] 而後就從這裏讀 |
ByteArrayOutputStream |
內部有一個byte buf[]緩衝區 |
構造方法初始化這個緩衝區,也就是分配空間 數據的寫,就是寫到這裏面 |
|
CharArray 內存數據 |
CharArrayReader |
內部有一個 char buf[]; 引用 指向實際保存數據的那個字符數組 |
CharArrayReader(char buf[]) public CharArrayReader(char buf[], int offset, int length) 構造方法將內部的char buf[]; 引用指向某個char buf[] 而後就從這裏讀 |
CharArrayWriter |
內部有一個char buf[] 緩衝區 |
構造方法初始化這個緩衝區,也就是分配空間
數據的寫,就是寫到這裏面
|
|
String 內存數據 |
StringReader |
內部有一個 String str;引用 指向實際的那個提供數據的String |
StringReader(String s) 構造方法將內部的str 引用指向某個String 而後就從這裏讀 |
StringWriter |
內部有一個StringBuffer buf 用於保存數據 |
public StringWriter()
StringWriter(int initialSize)
構造方法初始化這個StringBuffer,就是new StringBuffer時能夠指定大小
數據的寫,就是寫到這裏面 也就是StringBuffer.append
|
|
上面的這三種數據源形式,從上面看的話,邏輯很是清晰
讀--->從哪裏讀?--->你給我一個數據源--->我以IO的操做習慣(InputStream/Reader) 讀給你 |
寫--->IO的操做習慣寫(OutputStream/Writer) --->寫到哪裏?--->寫到我本身內部的存儲裏
|
有人可能以爲寫到你本身內部存儲裏面幹嗎,有毛用?
ByteArrayOutputStream |
|
CharArrayWriter |
|
StringWriter |
|
內存數據,若是僅僅是存起來放到他本身肚子裏面固然毛用沒有
可是,他們都提供了吐出來的功能了
給[字節數組 字符數組 String] 提供了一個統一的一致性的讀寫形式,操做很是方便,不是麼
|
真實數據使用引用指向
內部存儲是內部的存儲區
管道
pipe 管道用於直連 而後進行數據的傳輸 主要用於多線程數據共享
In 輸入管道里面有一個存儲區 Out 輸出管道內有個In的引用
Connect以後,In指向了某個實際的 輸入流
而後Out經過引用操做In裏面的存儲區 In本身的讀方法也是操做這個存儲區

|
Pipe |
PipedInputStream |
內部有存儲區byte buffer[] |
PipedInputStream() PipedInputStream(int pipeSize) 構造方法中給存儲區分配空間,能夠指定存儲區的大小,不然默認值 |
PipedOutputStream |
內部有一個PipedInputStream sink 引用 |
PipedOutputStream() PipedOutputStream(PipedInputStream snk) 無參的後續還須要調用connect 有參數的建立對象時就進行connect鏈接 |
PipedReader |
內部有存儲區 char buffer[]; |
PipedReader() PipedReader(int pipeSize) 構造方法中給存儲區分配空間,能夠指定大小,不然默認值 |
PipedWriter |
內部有一個PipedReader sink; 引用 |
PipedWriter() PipedWriter(PipedReader snk) 無參的後續還須要調用connect 有參數的建立對象時進行connect鏈接 |
|
因此一旦理解了,JavaIO管道的模型,管道就實在是太簡單了
|
只須要記住: 輸入In裏面 有一個存儲緩衝區, 輸出有一個引用指向了In connect將他們鏈接起來,他們共同操做一個池子 輸出往裏面寫,輸入從裏面讀 管子的方向只能是 : 輸出 -----> 輸入
|
文件
文件相關的,都是實實在在的要經過操做系統了 因此也就必然須要使用本地方法
在Java中一個文件使用File來描述,File是抽象路徑名 能夠表示文件 也能夠表示目錄 File能夠經過String路徑名構造 另外還有文件描述符能夠表示指代文件 |
File 磁盤數據 |
FileInputStream |
操做文件 構造方法可使用: File /String的路徑名 /文件描述符 來建立 實實在在的一個InputStream的實現類,最終經過本地方法來進行數據讀取 |
FileOutputStream |
操做文件 構造方法可使用: File/ String的路徑名 /文件描述符 來建立 另外他還有是否追加的概念 實實在在的一個OutputStream的實現類,最終經過本地方法來進行數據寫入 |
|
底層文件自己是二進制存儲的,若是你想要經過字符去操做文件,必然要通過 編碼和解碼的過程 |
|
JavaIO提供了InputStreamReader OutputStreamWriter兩個轉換流來實現編碼和解碼 想要完全理解仍是須要理解適配器模式 這兩個流都是字符和字節的轉換,只不過是方向不一樣 從字節到字符,這就是解碼 ; 從字符到字節,這就是編碼 |
InputStreamReader 字節流到字符流的橋樑, 也就是解碼 從上圖看,二進制纔是碼,從碼到字符
OutputStreamWriter 字符流到字節流的橋樑, 也就是編碼 從上圖看,二進制纔是碼,從字符到碼
|
根據上面的說法,FileReader 和 FileWriter必然要是一種轉換流
File
磁盤數據
|
FileReader |
文件自己都是二進制序列 字節形式,因此必然有字節InputStream到字符Reader的轉換
而InputStreamReader 偏偏是字節通向字符的橋樑
因此 FileReader繼承了InputStreamReader 是一種特殊的InputStreamReader
InputStreamReader 將InputStream適配成Reader 因此須要InputStream做爲參數進行構造
文件的字節輸入流--FileInputStream可使用: File /String的路徑名 /文件描述符 來建立
因此FileReader的構造方法接受這幾種參數, 構造一個FileInputStream
而後調用InputStreamReader 的構造方法
InputStreamReader字節到字符 解碼 涉及到碼錶 InputStreamReader構造方法部分須要指定編碼
|
FileWriter |
文件自己都是二進制序列 字節形式,因此想要寫入數據必然有字符Writer到字節OutputStream的轉換
而OutputStreamWriter 偏偏是字符通向字節的橋樑
因此 FileWriter繼承了OutputStreamWriter 是一種特殊的OutputStreamWriter
OutputStreamWriter 將OutputStream適配成Writer 因此須要OutputStream做爲參數進行構造
文件的字節輸出流 --FileOutputStream可使用: File /String的路徑名 /文件描述符 來建立
因此FileWriter的構造方法接受這幾種參數
而後構造一個FileOutputStream,調用OutputStreamWriter 的構造方法
另外FileOutputStream 還有追加的概念
因此FileWriter 的構造方法裏面也有append 追加這一項
OutputStreamWriter字符到字節 編碼 涉及到編碼 OutputStreamWriter構造方法部分須要指定編碼
|
|
關於FileReader 和FileWriter說了那麼多
其實只有兩句話,就是字節流與字符流之間進行轉換
Reader reader = new InputStreamReader( new FileInputStream(.......));
Writer writer = new OutputStreamWriter( new FileOutputStream(.......));
|
Object
對於文件中的字符與字節的轉換,能夠經過某些編碼表,查表法肯定編碼的值,進而完成字符與字節之間的相互轉換
那麼,對於一個對象呢?
Object是內存中的數據,他並非一串字符形式
有一個概念叫作 序列化與反序列化
其實就了相似 字符的編碼與解碼
|
|
從這個圖應該能感知到ObjectInputStream和ObjectOutputStream 與 字符流的邏輯相似麼 字符與字節轉換 是一種 編碼解碼的過程 對象序列化與反序列化 不也是一種編碼解碼的過程嗎 ,只不過這個編碼解碼不是單純的查詢碼錶這麼簡單 |
字符流與字節流的轉換,能夠經過轉換流進行處理 Object序列化與反序列化是 ObjectInputStream ObjectOutputStream 他們分別實現ObjectInput 和 ObjectOutput來提供的 因此從這個角度講的話,能夠把Object理解成爲一種很特殊的"字符" 他們兩個就像InputStreamReader 和 OutputStreamWriter似的,用來轉換 |
功能的裝飾擴展
既然是功能的裝飾擴展,咱們以前已經說過不少次,都是裝飾器模式
也就是說了不少遍的
這個你,就是須要被裝飾的抽象角色Component
就是這四你們族 InputStream OutputStream Reader Writer
給讀和寫裝飾增長新的功能,也就是最根本的讀和寫方法,將都是使用ConcreteComponent的
在基本的讀和寫方法之上,提供了新的功能
Data |
DataInputStream |
繼承自FilterInputStream 獲得一個InputStream引用in
構造方法須要InputStream |
經過in.read系列方法讀取, 而後將讀取的數據 組裝成基本數據類型 進而提供讀取基本數據類型的能力 |
DataOutputStream |
繼承自FilterOutputStream 獲得一個OutputStream 引用out
構造方法須要OutputStream |
將基本類型數據進行轉化處理, 而後調用out.write系列方法將數據寫入 進而提供寫入基本數據類型的能力 |
|
緩衝的概念都是內部有一個緩衝區
緩衝輸入 是經過底層的流往本身的緩衝區寫入數據, 應用程序從緩衝輸入的緩衝區中讀取,提升了read速度
緩衝輸出 是把數據寫入到本身的緩衝區中,後續再把數據經過底層的流一併寫入,從而提升了write的速度
由於讀寫都是從緩衝區中進行的了
Buffered |
BufferedInputStream |
繼承自FilterInputStream
獲得一個InputStream引用in
構造方法須要InputStream
內部有一個緩衝區byte buf[]
|
BufferedOutputStream |
繼承自FilterOutputStream
獲得一個OutputStream 引用out
構造方法須要OutputStream
內部有一個緩衝區buf[];
|
BufferedReader |
內部有Reader 引用 in 構造方法須要一個Reader
內部有一個緩衝區char cb[]; |
BufferedWriter |
內部有一個Writer 引用 out 構造方法須要一個Writer
內部有一個緩衝區char cb[]; |
|
LineNumberReader 內部使用了一個lineNumber = 0; 用來記錄行號 這個行號可使用方法設置和獲取 getLineNumber setLineNumber 可是他不改變流的位置 |
PushBack
裝飾器模式 方法依賴於被裝飾的實體 ConcreteComponent
只是內部有一個緩衝區,能夠存放被回退掉的字符
全部的讀取方法在進行讀取的時候,都會查看緩衝區的數據
PushbackInputStream
|
繼承自FilterInputStream 獲得一個InputStream 引用in 構造方法須要 InputStream
內部有緩衝區byte[] buf |
FilterReader |
繼承自FilterReader 獲得一個Reader引用 in 構造方法須要一個Reader
內部有緩衝區char[] buf |
Print
提供了多種形式的打印,根本只是在真的寫入數據前,將數據參數進行一些處理
根本的寫操做 依賴被裝飾的節點流提供
在數據寫入以前進行必要的數據處理
PrintStream |
繼承自 FilterOutputStream獲得一個OutputStream 引用 out 構造須要一個OutputStream |
PrintWriter |
內部有一個out 構造方法須要一個Writer |
因此你看,擴展的功能經過裝飾器模式,他們的行爲都是相似的,那就是:
1. 最基本的讀寫依賴被裝飾的具體的節點流
2. 而後進行了功能的加強
總結
說到這個地方,咱們又從實現的角度把經常使用的一些流進行了介紹
你會發現看起來那麼多,實際上並無多少
四你們族,以及幾種數據源形式,以及幾個擴展功能點
只要找準了思路,理清楚了邏輯,就不難理解了
不知道究竟是恍然大悟?仍是?恍然如夢 ?