io詳解

一、什麼是流

       首先什麼是流:流(Stream)的概念來源於UNIX中的管道(pipe)概念,在unix中,管道是一條不間斷的字節流,用來實現程序和進程間的通信,或者讀寫外圍設備、外部文件等。流,必須有源端和目的端,可以是文件,內存或者網絡等。流的創建是爲了更方便的處理數據的輸入輸出。

       其次,對於輸入流輸出流如何區分,相信有很多人鬧不清楚,本人也是很長時間弄不太明白,簡單的來說,輸入輸出均是相對於內存來說,這樣就比較好理解了,簡單的來說:輸入流就是從外部文件輸入到內存,輸出流主要是從內存輸出到文件。

        第三,類別,流可以分爲字節流和字符流。字節流爲原始數據,需要用戶讀入後進行相應的編碼轉換,而字符流的實現是居於自動轉換的,讀取數據時會把數據按照JVM的默認編碼自動轉換成字符。字節流由inputStream和outputStream處理(注意,這個類均是抽象類,實現的是他們的子類),爲了讓數據處理的更方便,java在後期版本中加上了字符流的Reader和Writer類。


二、字節流:InputStream和OutputStream

        程序可以從中連續讀取字節的對象叫輸入流,用InputStream類完成,程序能向其中連續寫入字節的對象叫輸出流,用OutputStream類完成。InputStream與OutputStream對象是兩個抽象類,還不能表明具體對應哪種IO設備。它們下面有許多子類,包括網絡,管道,內存,文件等具體的IO設備,如FileInputStream類對應的就是文件輸入流,是一個節點流類,我們將這些節點流類所對應的IO源和目標稱爲流節點(Node)。

        

        InputStream定義了Java的輸入流模型。該類中的所有方法在遇到錯誤的時候都會引發IOException異常,下面是InputStream類中方法的一個簡要說明:

     

[plain] view plain copy
  1. int read()返回下一個輸入字節的整型表示,,如果返回-1表示遇到流的末尾,結束。  
  2.       int read(byte[]b)讀入b.length個字節放到b中並返回實際讀入的字節數。  
  3.      int read(byte[]b,int off,int len) 這個方法表示把流中的數據讀到,數組b中,第off個開始的len個數組元素中.  
  4.      long skip(long n) 跳過輸入流上的n個字節並返回實際跳過的字節數。  
  5.       int availabale() 返回當前輸入流中可讀的字節數。  
  6.      void mark(int readlimit)在輸入流的當前位置處放上一個標誌,允許最多再讀入readlimit個字節。  
  7.         void reset() 把輸入指針返回到以前所做的標誌處。  
  8.       boolean markSupported() 如果當前流支持mark/reset操作就返回true。  
  9.      void close()  在操作完一個流後要使用此方法將其關閉,系統就會釋放與這個流相關的資源。  

InputStream是一個抽象類,程序中實際使用的是它的各種子類對象。不是所有的子類都會支持InputStream中定義的某些方法的,如skip,mark,reset等,這些方法只對某些子類有用。

        注意點:

        一個對象在沒有引用變量指向它時會變成垃圾,最終會被垃圾回收器從內存中清除。對於我們創建的流對象,幹嘛還要“調用close方法將它關閉,以釋放與其相關的資源”呢?這相關的資源到底是些什麼呢?我們在程序中創建的對象都是對應現實世界中有形或無形的事物,計算機操作系統所產生的東西當然也是現實世界中的事物,也就是說,程序中的對象也可以對應計算機操作系統所產生的一個其他東西,專業地說,這些東西叫資源,流就是操作系統產生的一種資源。當我們在程序中創建了一個IO流對象,同時系統內也會創建了一個叫流的東西,在這種情況下,計算機內存中實際上產生了兩個事物,一個是Java程序中的類的實例對象,一個是系統本身產生的某種資源,我們以後講到的窗口,Socket等都是這樣的情況。Java垃圾回收器只能管理程序中的類的實例對象,沒法去管理系統產生的資源,所以程序需要調用close方法,去通知系統釋放其自身產生的資源。


          OutputStream是一個定義了輸出流的抽象類,這個類中的所有方法均返回void,並在遇到錯誤時引發IOException異常。下面是OutputStream的方法:

        

[plain] view plain copy
  1. void write(int b) 將一個字節寫到輸出流。注意,這裏的參數是int型,它允許write使用表達式而不用強制轉換成byte型。  
  2.         void write(byte[] b) 將整個字節數組寫到輸出流中。  
  3.         void write(byte [] b,int off,int len) 將字節數組b中的從off開始的len個字節寫到輸出流。  
  4.         void flush徹底完成輸出並清空緩衝區。  
  5.         void close關閉輸出流。  

       下面是從網上找到的它們的結構圖:




三、字符流Reader和write

       

        Java中的字符是unicode編碼,是雙字節的,而InputStream與OutputStream是用來處理字節的,在處理字符文本時不太方便,需要編寫額外的程序代碼。Java爲字符文本的輸入輸出專門提供了一套單獨的類,Reader、Writer兩個抽象類與InputStream、OutputStream兩個類相對應,同樣,Reader、Writer下面也有許多子類,對具體IO設備進行字符輸入輸出,如FileReader就是用來讀取文件流中的字符。

對於Reader和Writer,我們就不過多的說明了,大體的功能和InputStream、OutputStream兩個類相同,但並不是它們的代替者,只是在處理字符串時簡化了我們的編程。

       簡單例子:

       

[java] view plain copy
  1. import java.io.*;  
  2. public class FileStream  
  3. {  
  4.     public static void main(String[] args)  
  5.     {  
  6.         File f = new File("hello.txt");   
  7.         try  
  8.         {  
  9.             FileWriter out = new FileWriter(f);  
  10.             out.write("www.it315.org");  
  11.             out.close();  
  12.         }  
  13.         catch(Exception e)  
  14.         {  
  15.             System.out.println(e.getMessage());  
  16.         }  
  17.           
  18.         try  
  19.         {  
  20.             FileReader in = new FileReader(f);  
  21.             char [] buf = new char[1024];  
  22.             int len = in.read(buf);  
  23.             System.out.println(new String(buf,0,len));  
  24.         }  
  25.         catch(Exception e)  
  26.         {  
  27.             System.out.println(e.getMessage());  
  28.         }  
  29.     }  
  30. }  

四、字節流與字符流的轉換

前面我們講過,Java支持字節流和字符流,我們有時需要字節流和字符流之間的轉換。

InputStreamReader 和OutputStreamWriter

這兩個類是字節流和字符流之間轉換的類,InputStreamReader可以將一個字節流中的字節解碼成字符,OuputStreamWriter將寫入的字符編碼成字節後寫入一個字節流。其中InputStreamReader有兩個主要的構造函數:

[plain] view plain copy
  1. InputStreamReader(InputStream in) //用默認字符集創建一個InputStreamReader對象  
  2. InputStreamReader(InputStreamin,String CharsetName) //接受以指定字符集名的字符串,並用//該字符集創建對象  
  3. OutputStreamWriter也有對應的兩個主要的構造函數:  
  4. OutputStreamWriter(OutputStream in) //用默認字符集創建一個OutputStreamWriter對象  
  5. OutputStreamWriter(OutputStream in,StringCharsetName) //接受以指定字符集名的字符串,  
  6. //並用該字符集創建OutputStreamWriter對象  

爲了達到最好的效率,避免頻繁的字符與字節間的相互轉換,我們最好不要直接使用這兩個類來進行讀寫,應儘量使用BufferedWriter類包裝OutputStreamWriter類,用BufferedReader類包裝InputStreamReader。例如:

  1. BufferedWriter out=new BufferedWriter(newOutputStreamWriter(System.out));  
  2. BufferedReader in=new BufferedReader(new InputStreamReader(System.in));  

我們接着從一個更實際的應用中來熟悉InputStreamReader的作用,怎樣用一種簡單的方式一下就讀取到鍵盤上輸入的一整行字符?只要用下面的兩行程序代碼就可以解決這個問題:

  1. BufferedReader in=new BufferedReader(new InputStreamReader(System.in));  
  2. String strLine = in.readLine();  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章