[二十六]JavaIO之再回首恍然(如夢? 大悟?)



流分類回顧


本文是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 image_5b9b6870_54fc
CharArrayWriter image_5b9b6870_1ff1
StringWriter image_5b9b6870_5824

內存數據,若是僅僅是存起來放到他本身肚子裏面固然毛用沒有
可是,他們都提供了吐出來的功能了
給[字節數組 字符數組  String] 提供了一個統一的一致性的讀寫形式,操做很是方便,不是麼

image_5b9b6870_7808
真實數據使用引用指向
內部存儲是內部的存儲區



管道

pipe 管道用於直連 而後進行數據的傳輸
主要用於多線程數據共享

In 輸入管道里面有一個存儲區
Out 輸出管道內有個In的引用

Connect以後,In指向了某個實際的 輸入流

而後Out經過引用操做In裏面的存儲區
In本身的讀方法也是操做這個存儲區
image_5b9b6870_f2f


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管道的模型,管道就實在是太簡單了
image_5b9b6870_4d4f
只須要記住:
輸入In裏面 有一個存儲緩衝區, 輸出有一個引用指向了In
connect將他們鏈接起來,他們共同操做一個池子
輸出往裏面寫,輸入從裏面讀
管子的方向只能是 :    輸出 -----> 輸入





文件
文件相關的,都是實實在在的要經過操做系統了
因此也就必然須要使用本地方法

在Java中一個文件使用File來描述,File是抽象路徑名 能夠表示文件  也能夠表示目錄
File能夠經過String路徑名構造
另外還有文件描述符能夠表示指代文件


File
磁盤數據
FileInputStream 操做文件
構造方法可使用:  File /String的路徑名 /文件描述符   來建立
實實在在的一個InputStream的實現類,最終經過本地方法來進行數據讀取
FileOutputStream 操做文件
構造方法可使用: File/ String的路徑名 /文件描述符     來建立
另外他還有是否追加的概念
實實在在的一個OutputStream的實現類,最終經過本地方法來進行數據寫入


底層文件自己是二進制存儲的,若是你想要經過字符去操做文件,必然要通過 編碼和解碼的過程
image_5b9b6870_6d14
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是內存中的數據,他並非一串字符形式
有一個概念叫作         序列化與反序列化
其實就了相似  字符的編碼與解碼
image_5b9b6870_3444
從這個圖應該能感知到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. 而後進行了功能的加強


總結

說到這個地方,咱們又從實現的角度把經常使用的一些流進行了介紹
你會發現看起來那麼多,實際上並無多少
四你們族,以及幾種數據源形式,以及幾個擴展功能點
只要找準了思路,理清楚了邏輯,就不難理解了

不知道究竟是恍然大悟?仍是?恍然如夢 ?
相關文章
相關標籤/搜索