1. 程式人生 > >Java IO庫簡介

Java IO庫簡介

一、流的概念

Java API中,可以從其中讀入一個位元組序列的物件稱作輸入流,而可以向其中寫入一個位元組序列的物件稱作輸出流。這些位元組序列的來源地和目的地可以是檔案,而且通常都是檔案,但是也可以是網路連線,甚至是記憶體塊。程式語言的I/O類庫中使用流這個抽象概念,它代表任何有能力產出資料的資料來源物件或者是有能力接收資料的接收端物件。

簡單的理解:流是資料和資料處理過程的統稱。

流操作關心三部分內容:資料來源、目標以及過程。

這些資料來源包括:

1)      位元組陣列

2)      String物件

3)      檔案

4)      “管道”,工作方式與實際管道相似,即,從一段輸入,從另一端輸出。

5)      一個由其他種類的流組成的序列,以便我們可以將它們收集合併到一個流內。

6)      其他資料來源,如Internet連線等。

二、流的分類

1.  按流的方向:輸入流和輸出流

可以從其中讀入一個位元組序列的物件稱作輸入流,而可以向其中寫入一個位元組序列的物件稱作輸出流。入和出都是相對於程式本身來說的(不是相當於流本身,因為程式中的“入”,是將流中的資料讀取到程式中,對流來說此時是“出”),比如,在程式中使用一個檔案儲存資料,進行的操作是向流中寫入資料儲存到檔案,此時的流是輸出流從流中讀取資料到程式的流稱為輸入流


上面的概念中也反映了該類流的特點:對輸入流只能進行讀操作,對輸出流只能進行寫操作。

1.  按資料單位:位元組流和字元流

在電腦磁碟上,所有的資料都是以位元組的形式傳輸和儲存,包括圖片、視訊、文件等資料。一個位元組佔用8位二進位制(8bit),而一個字元根據編碼方式的不同,佔用的儲存空間是不同的。有句話很適合現在:基本的才是萬能的。所以位元組流能夠處理任何型別的資料,而字元流只能處理字元型別的資料。

2.  按功能:節點流和過濾流

節點流是使用原始的流類進行的操作,而過濾流是在節點流的基礎上進行修飾以獲得更多的功能,這裡使用了裝飾器模式。

三、輸入流和輸出流層次結構

位元組流的頂級父類是抽象類InputStream和OutputStream,輸入輸出流的層次結構如下圖:

Reader和Writer的層次結構如下圖:


Java類庫中的I/O類分成輸入和輸出兩部分,可以在JDK的文件裡的類層次結構中檢視到。通過繼承,任何自InputStream或Reader派生而來的類都含有名為read()的基本方法,用於讀取單個位元組或位元組陣列。同樣,任何自OutputStream或Writer類派生未來的類都含有名為write()的基本方法,用於寫單個位元組或者位元組陣列。但是,我們通常不會用到這些方法,他們之所以存在是因為別的類可以使用它們,以便提供更有用的介面。因此,我們很少使用單一的類來建立流物件,而是通過疊合多個物件來提供所期望的功能(這就是裝飾器設計模式)。實際上,Java“流”類庫讓人迷惑的主要原因就在於:建立單一的結果流,卻需要建立多個物件。

FilterInputStream和FilterOutputStream是用來提供裝飾器類介面以控制特定輸入流額輸出流的兩個類,他們的名字不是很直觀。FilterInputStream和FilterOutputStream分別自I/O類庫中的積累InputStream和OutputStream派生而來,這兩個類是裝飾器的必要條件(以便能為所有正在被修飾的物件提供通用介面)。

FilterInputStream類能夠完成兩件完全不同的事情。其中,DataInputStream允許我們讀取不同的基本型別資料以及String物件(所有方法都以“read”開頭,例如readByte()、readFloat()等等)。搭配相應的DataInputStream,我們就可以通過資料“流”將基本型別的資料從一個地方遷移到另一個地方。

其他FilterInputStream類則是在內部修改InputStream的行為方式:是否緩衝,是否保留它所讀過的行(允許我們查詢行數或設定行數),以及是否把單一字元推回輸入流等等。最後兩個類看起來更像是為了建立一個編譯器(它們被新增進來可能是為了對“用Java構建編譯器”實驗提供支援),因此我們在一般程式設計中不會用到它們。

我們幾乎每次都要對輸入 進行緩衝——不管我們正在連線的是什麼I/O裝置,所以,I/O類庫把無緩衝輸入(而不是緩衝輸入)作為特殊情況(或只是方法呼叫)就顯得更加合理了。

四、Java IO分類介紹

4.1 位元組流

4.1.1位元組輸入流

InputStream表示位元組輸入流的所有類的超類,

FileInputStream 從檔案系統中的某個檔案中獲得輸入位元組。

ByteArrayInputStream 包含一個內部緩衝區,該緩衝區包含從流中讀取的位元組。內部計數器跟蹤read 方法要提供的下一個位元組。關閉ByteArrayInputStream 無效。此類中的方法在關閉此流後仍可被呼叫,而不會產生任何IOException

PipedInputStream管道輸入流應該連線到管道輸出流;管道輸入流提供要寫入管道輸出流的所有資料位元組。通常,資料由某個執行緒從PipedInputStream 物件讀取,並由其他執行緒將其寫入到相應的PipedOutputStream。不建議對這兩個物件嘗試使用單個執行緒,因為這樣可能死鎖執行緒。管道輸入流包含一個緩衝區,可在緩衝區限定的範圍內將讀操作和寫操作分離開。如果向連線管道輸出流提供資料位元組的執行緒不再存在,則認為該管道已損壞

FilterInputStream 包含其他一些輸入流,它將這些流用作其基本資料來源,它可以直接傳輸資料或提供一些額外的功能。

ObjectInputStream 對以前使用 ObjectOutputStream 寫入的基本資料和物件進行反序列化。

SequenceInputStream 表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達檔案末尾,接著從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的檔案末尾為止。

BufferedInputStream 為另一個輸入流新增一些功能,即緩衝輸入以及支援mark 和reset 方法的能力。在建立BufferedInputStream 時,會建立一個內部緩衝區陣列。在讀取或跳過流中的位元組時,可根據需要從包含的輸入流再次填充該內部緩衝區,一次填充多個位元組。mark 操作記錄輸入流中的某個點,reset 操作使得在從包含的輸入流中獲取新位元組之前,再次讀取自最後一次mark 操作後讀取的所有位元組。

DataInputStream資料輸入流允許應用程式以與機器無關方式從底層輸入流中讀取基本 Java 資料型別。應用程式可以使用資料輸出流寫入稍後由資料輸入流讀取的資料。

PushbackInputStream 為另一個輸入流新增效能,即“推回 (push back)”或“取消讀取 (unread)”一個位元組的能力。

4.2.2位元組輸出流

OutputStream此抽象類是表示輸出位元組流的所有類的超類。

ByteArrayOutputStream此類實現了一個輸出流,其中的資料被寫入一個 byte 陣列。緩衝區會隨著資料的不斷寫入而自動增長。可使用toByteArray() 和toString() 獲取資料。關閉ByteArrayOutputStream 無效。

FileOutputStream檔案輸出流是用於將資料寫入 File 或FileDescriptor 的輸出流。

FilterOutputStream此類是過濾輸出流的所有類的超類。

ObjectOutputStream 將 Java 物件的基本資料型別和圖形寫入 OutputStream。

PipedOutputStream可以將管道輸出流連線到管道輸入流來建立通訊管道。管道輸出流是管道的傳送端。

BufferedOutputStream該類實現緩衝的輸出流。通過設定這種輸出流,應用程式就可以將各個位元組寫入底層輸出流中,而不必針對每次位元組寫入呼叫底層系統。

DataOutputStream資料輸出流允許應用程式以適當方式將基本 Java 資料型別寫入輸出流中。然後,應用程式可以使用資料輸入流將資料讀入。

4.2 字元流

4.2.1字元輸入流

Reader 用於讀取字元流的抽象類。

BufferedReader從字元輸入流中讀取文字,緩衝各個字元,從而實現字元、陣列和行的高效讀取。

LineNumberReader跟蹤行號的緩衝字元輸入流。此類定義了方法setLineNumber(int) 和 getLineNumber(),它們可分別用於設定和獲取當前行號。

CharArrayReader此類實現一個可用作字元輸入流的字元緩衝區。

InputStreamReader 是位元組流通向字元流的橋樑:它使用指定的 charset 讀取位元組並將其解碼為字元。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺預設的字符集。

FileReader用來讀取字元檔案的便捷類。此類的構造方法假定預設字元編碼和預設位元組緩衝區大小都是適當的。

StringReader其源為一個字串的字元流。

PipedReader傳送的字元輸入流。

FilterReader用於讀取已過濾的字元流的抽象類。

4.2.2字元輸出流

Writer寫入字元流的抽象類。

BufferedWriter將文字寫入字元輸出流,緩衝各個字元,從而提供單個字元、陣列和字串的高效寫入。

CharArrayWriter此類實現一個可用作Writer 的字元緩衝區。緩衝區會隨向流中寫入資料而自動增長。可使用 toCharArray() 和 toString() 獲取資料。在此類上呼叫 close() 無效。

OutputStreamWriter是字元流通向位元組流的橋樑:可使用指定的 charset 將要寫入流中的字元編碼成位元組。它使用的字符集可以由名稱指定或顯式給定,否則將接受平臺預設的字符集。

         FileWriter用來寫入字元檔案的便捷類。

PipedWriter傳送的字元輸出流。

PrintWriter向文字輸出流列印物件的格式化表示形式。此類實現在 PrintStream 中的所有 print 方法。它不包含用於寫入原始位元組的方法,對於這些位元組,程式應該使用未編碼的位元組流進行寫入。        

StringWriter一個字元流,可以用其回收在字串緩衝區中的輸出來構造字串。關閉 StringWriter 無效。

FilterWriter用於寫入已過濾的字元流的抽象類。

五、示例程式碼

1.緩衝輸入檔案

[java] view plain copy  print?
  1. publicstatic String read(String filename) throws IOException{  
  2.     BufferedReader in = new BufferedReader(new FileReader(filename));  
  3.     String s;  
  4.     StringBuilder sb = new StringBuilder();  
  5.     while((s = in.readLine())!=null){  
  6.         sb.append(s + "\n");  
  7.     }  
  8.     in.close();  
  9.     return sb.toString();  
  10. lt;span style="white-space:pre"> </span>}  

2.      從記憶體輸入

[java] view plain copy  print?
  1. <span style="white-space:pre">    </span>publicstaticvoid memoryInput(String filename) throws IOException{  
  2.         StringReader in = new StringReader(BufferedInputFile.read(filename));  
  3.         int c;  
  4.         while((c = in.read())!=-1){  
  5.             System.out.print((char)c);  
  6.         }  
  7.     }  

3.格式化的記憶體輸入

[java] view plain copy  print?
  1. <span style="white-space:pre">    </span>publicstaticvoid formatMemoryInput(String filename) throws IOException{  
  2.         try{  
  3.         DataInputStream in = new DataInputStream(new ByteArrayInputStream(BufferedInputFile.read(filename).getBytes()));  
  4.         while(true){  
  5.             System.out.print((char)in.readByte());  
  6.         }  
  7.         }catch(IOException e){  
  8.             System.out.println("End of stream");  
  9.         }  
  10.     }  

4.一次一個位元組的讀取檔案

[java] view plain copy  print?
  1. publicstaticvoid testEOF(String filename) throws IOException{  
  2.     DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(filename)));  
  3.     while(in.available() != 0){  
  4.         System.out.print((char)in.readByte());  
  5.     }  
  6.     in.close();  
  7. }  

5.基本的檔案輸出

[java] view plain copy  print?
  1. publicstaticvoid basicFileOut(String filename , String fileout) throws IOException{  
  2.     BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read(filename)));  
  3.     PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileout)));  
  4.     int lineCount = 1;  
  5.     String s;  
  6.     while((s = in.readLine())!=null){  
  7.         out.println(lineCount++ + ":" + s);  
  8.     }  
  9.     out.close();  
  10.