Java IO

傲慢的上校發表於2012-09-30

         以下資料,大部分為自己整理而來,供學習之用!



一、什麼是流

       首先什麼是流:流(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類中方法的一個簡要說明:

     

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

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

        注意點:

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


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

        

void write(int b) 將一個位元組寫到輸出流。注意,這裡的引數是int型,它允許write使用表示式而不用強制轉換成byte型。
        void write(byte[] b) 將整個位元組陣列寫到輸出流中。
        void write(byte [] b,int off,int len) 將位元組陣列b中的從off開始的len個位元組寫到輸出流。
        void flush徹底完成輸出並清空緩衝區。
        void close關閉輸出流。

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




三、字元流Reader和write

       

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

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

       簡單例子:

       

import java.io.*;
public class FileStream
{
	public static void main(String[] args)
	{
		File f = new File("hello.txt"); 
		try
		{
			FileWriter out = new FileWriter(f);
			out.write("www.it315.org");
			out.close();
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
		}
		
		try
		{
			FileReader in = new FileReader(f);
			char [] buf = new char[1024];
			int len = in.read(buf);
			System.out.println(new String(buf,0,len));
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
		}
	}
}

四、位元組流與字元流的轉換

前面我們講過,Java支援位元組流和字元流,我們有時需要位元組流和字元流之間的轉換。

InputStreamReader 和OutputStreamWriter

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

InputStreamReader(InputStream in) //用預設字符集建立一個InputStreamReader物件
InputStreamReader(InputStreamin,String CharsetName) //接受以指定字符集名的字串,並用//該字符集建立物件
OutputStreamWriter也有對應的兩個主要的建構函式:
OutputStreamWriter(OutputStream in) //用預設字符集建立一個OutputStreamWriter物件
OutputStreamWriter(OutputStream in,StringCharsetName) //接受以指定字符集名的字串,
//並用該字符集建立OutputStreamWriter物件

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

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

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

BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String strLine = in.readLine();