Java-I/O 設計和類的梳理

在學習JAVA的時候,被I/O整蒙了,一個重要的原因是數量多的不同類,卻完成相似的功能。另外一個是同樣的目的可以由多種方式來實現,根本還是和類的設計有關。
近日,在網上看到一篇文章,清晰的梳理了Java I/O設計思路,個人認爲很棒。

這裏先做設計和類的梳理,具體代碼方面等細節問題,這裏不涉及,等再開一片。

個人的總結:
1. stream代表一種數據流,具體是輸入還是輸出,使用哪個類,首先需要看功能其次要注意使用的類的名字最後代碼細節

2. 對於類的使用,主要區分是InputStream OR OutputStream
 一種區分方法:這裏的主體是CPU,如果需要獲得數據到內存中(比如從文件),就要使用InputStream/Reader的相關類。反之,使用OutputStream/Writer的相關類。
   
   另一種區分方法: 任何有能力產生數據流(源)的javaio對象就可以看作是一個InputStream對象。
                                 任何有能力接收數據源(流)的javaio對象我們就可以看作是一個OutputStream對象。

3. 對於細節,要注意根據需要使用封裝的類(嵌套)。在就是要注意關閉,一般都要寫到finally中。



Summary 總述

java.io包中的類非常繁多,但其實只要歸成4類:InputStream & OutputStreamReader & Writer,由於功能和命名上都相當接近,因此只要掌握了其中一種,將會很容易理解其他3種。

橫向歸類:InputStream & OutputStream(字節流)、Reader & Writer(字符流).
縱向歸類:InputStream作爲父類,其子類的角色和作用,並以此舉一反三。

字節流

字節(byte)是計算機中基本數據單位,一切的計算機數據(或“文件”)都是由或多或少的字節組成,因此使用字節流,理論上可以處理一切計算機數據(文件),包括圖像、音頻、文本等。

然而對於文本數據,由於存在編碼問題比較麻煩,所以交由字符流處理。

字符流

1char=2byte,字符(char)的表示範圍(2^16)是字節(byte)表示範圍(2^8)的2^8=256倍。專門用於處理文本數據。

歷史

字節流在Java的初版(jdk 1.0)已經存在,字符流在jdk 1.1中加入,以替代字節流中處理字符的功能。

裝飾者模式

私以爲,提到Java I/O的話,不能不提裝飾者模式

裝飾者模式就是在一個主體(被裝飾者)的外部使用裝飾類來進行裝飾,對主體的行爲根據不同的裝飾者類進行不同的修改。單個的裝飾類自根據自身特點對主體的行爲進行部分改進,因此可以組合多個裝飾類來對主體進行修改(在代碼中表現爲多層裝飾類的嵌套)。

Java的I/O類設計應用了裝飾者模式。單個的流對象主體,例如InputStream的直接子類(Direct Subclasses)中,分別面向文件FileInputStream、內存ByteArrayInputStream、線程PipedInputStream,而InputStream的另一個直接子類FileterInputStream爲裝飾類(的父類),分別定義了各種具體的裝飾類(如BufferedInputStreamDataInputStream等)。

如圖: Alt text

從代碼的角度來看:DataInputStreamBufferedInputStream爲裝飾者,FileInputStream爲主體(被裝飾者)

InputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(src))));

裝飾者模式從抽象的角度來說很容易理解,然而也存在一個很明顯的缺點:裝飾類過多。因此我們能看到java.io包中那麼多的類。如果沒有理解裝飾者模式,即使有一定經驗的Java developer也會感到混亂。

結論

  • 處理非文本數據使用字節流;
  • 處理具體的文本數據使用字符流;

例如對於文件複製這樣的操作來說,即使是文本文件的複製,我們對其具體的內容是什麼並不關心,因此可以直接使用字節流。但當我們要從一個文本文件中讀取內容,我們關心其具體的內容,所以使用字符流。


Description 詳細內容

篇幅所限,只列出常用的類,其餘部分可參考Java API手冊

繼承關係圖

Alt text

字節流的繼承關係

OutputStream

  • OutputStream
    • ByteArrayOutputSteam:將數據輸出到字節數組(byte array)中,也就是內存,不用生成文件;
    • FileOutputStream:將數據輸出到具體的文件;
    • PipedOutputStream:將數據輸出到線程,即通過與PipedInputStream聯合使用,將數據在不同的線程之間傳遞;
    • FilterOutputStream:裝飾類的父類
      • BufferedOutputStream:使用了緩衝區,調用flush()纔會清空緩衝區將數據寫入文件。與普通OutputStream相比,由於不用頻繁地與文件進行I/O數據傳輸(內存與磁盤之間,這將消耗大部分性能),而是在每次調用flush()時一次性地將一塊數據在內存與磁盤中傳輸,因此會性能將得到提升(有NIO的影子);
      • DataOutputStream:用於方便地傳輸基礎類型的數據,因此除了傳統的write()外,還有一堆writeInt()、writeDouble、writeBoolean()等;
      • PrintStream:InputStream本來是適合用於非文本的二進制文件(如圖片、聲音文件等),而PrintStream則是在字節流中專門用於打印文本內容;

InputStream

  • InputStream
    • ByteArrayInputStream:從內存中讀取數據;
    • FileInputStream:從文件中讀取數據;
    • PipedInputStream:從一個線程中讀取數據,從另一個線程中輸出(PipedOutputStream),同一線程下使用者兩個對象可能會造成線程死鎖;
    • SequenceInputStream:將兩個InputStream合併成一個;
    • FilterInputStream:裝飾類的父類;
      • BufferedInputStream:使用了緩衝區,參考BufferedOutputStream
      • DataInputStream:方便地讀取基本類型的數據,因此除了基礎的read()外,還有一堆readChar()、readDouble()、readInt()等;

字符流的繼承關係

Writer

  • BufferedWriter:緩衝區;
  • CharArrayWriter:面向內存;
  • PipedWriter:面向線程;
  • PrintWriter:方便輸出,特別是按格式輸出printf();
  • StringWriter:使用StringBuilder來存放內容;
  • OutputStreamWriter:用於字符流與字節流之間的轉換;
    • FileWriter:面向文件
  • FilterWriter:裝飾類

Reader

  • BufferedReader:緩衝區;
  • CharArrayReader:面向內存;
  • PipedReader:面向線程;
  • InputStreamReader:用於字符流與字節流之間的轉換;
    • FileReader:面向文件
  • FilterReader

參考
http://my.oschina.net/SCVTheDefect/blog/531127
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章