java中基本輸入輸出流的解釋

fsz521job發表於2005-07-11

網路程式的很大一部分是簡單的輸入輸出,即從一個系統向另一個系統移動位元組。位元組就是位元組,在很大程度上,讀伺服器傳送的資料與讀取檔案沒什麼不同;向客戶傳送資料與寫入一個檔案也沒有什麼區別。

Java中輸入和輸出組織不同於大多數其他語言。它是建立在流(stream)上。不同的基本流類(java.io.FileInputStreamsun.net.TelnetOutputStream)用於讀寫特定的資料資源。但是所有的基本輸出流使用同一種基本方法讀資料。

過濾器流可以連線到輸入流或輸出流。它可以修改已經讀出或寫人的資料(例如,加密或壓縮資料),或者可以簡單地提供附加方法將已經讀出或寫入的資料轉化成其他格式。

最後ReaderWriter也可以連結到輸入流和輸出流,從而允許程式讀出和寫入文字(即字元)而不是位元組。如果使用正確,ReaderWriter能夠處理多種型別的字元編碼,包括SJISUTF-8等多位元組字符集。

[@more@]

一、輸出流
java的基本輸出流是 java.io.OutputStream.public abstract class OutputStream

n public abstract void write(int b) throws IOExceptionn public void write(byte[] data) throws IOExceptionn public void write(byte[] data,int offset,int length) throws IOExceptionn public void flush() throws IOExceptionn public void close() throws IOExceptionOutputStream的子類使用這些方法向指定媒體寫入資料。

我始終相信,我們理解了為什麼它們存在,就會更好地記住它們,好,現在開始說一下OutputStream類的方法的由來Ø public abstract void write(int b) throws IOException
OutputStream的基本方法是write(int b)。該方法將介於0到255之間的整數看作變數,並將相應的位元組寫到一個輸出流。該方法宣告是個抽象方法,因為子類需要改變它以處理特定媒體。例如,ByteArrayOutputStream可以使用複製位元組到其陣列的純Java程式碼來實現方法。但是,FileOutputStream就需要使用程式碼,此程式碼應該理解如何在主機平臺上將資料寫入檔案。注意:儘管該方法把整形值作為變數,但是它實際上寫入的是一個無符號位元組。Java沒有無符號位元組資料型別,因此這裡使用整型來代替。無符號位元組和有符號位元組之間的真正區別是編譯器對它們的解釋。二者都是由8位組成,並且當使用write(int b)將一個int寫入到網路連線流時,只有8位資料傳送。如果將一個超出0-255範圍的int傳給write(int b),則寫入該數字的低位位元組,而忽略餘下的三個位元組(大家都知道java的int是4個位元組的,這裡本質就是將int轉換為byte)。

Ø public void write(byte[] data) throws IOException和public void write(byte[] data,int offset,int length) throws IOException
每次寫入一個位元組通常效率不高。因此,大部分TCP/IP程式將資料存入一定長度的緩衝區,即在記憶體中累積位元組,並僅當累積了一定數目位元組或過了一定的時間段,才將它們傳送到最終的目的地。因此write(byte[] data)和write(byte[] data,int offset,int length)就是這樣產生了。

Ø public void flush() throws IOException
我們可以在軟體中或直接在Java程式碼中對流實施緩衝操作,也可以在網路硬體中對流實施緩衝操作。就好像BufferedOutputStream或BufferedWriter連結到底層流來實現流緩衝。因此,如果正在寫入資料,則重新整理輸出流是相當重要的。例如,假設已經寫入了一個300位元組的請求給一個HTTP Keep-Alive的HTTP伺服器,通常希望在傳送更多資料之間等待響應。但是,如果輸出流有一個1024位元組的緩衝區,則該流可能在將資料傳送出緩衝區之前正在等待更多的資料到達,但是這些資料似乎不會到達的,因為它們還沒有傳送出去,但是緩衝流不會傳送資料給伺服器,除非它從底層流獲得更多的資料,但是底層流不會傳送更多的資料,除非它從伺服器獲得資料,而伺服器不會傳送資料,除非它獲得保留在緩衝區中的資料(死鎖了!),flush()方法就可以解決了這個僵局,因為即使緩衝區未滿,他也會強制要求實行緩衝操作的流傳送資料。注意:是否對流實行了緩衝操作,這決定於你如何獲得指向流的引用(例如,不論是否希望對System.out執行緩衝操作,都會對其實施緩衝)。如果重新整理流需要重新整理時,就必須重新整理,但是如果重新整理失敗了就會導致不可預料、不可重複的程式掛起(flush()返回值是void啊),如果事先不瞭解掛起問題所在,就很難解決這個問題了。因此,在關閉所有流之前,應當立即重新整理它們。否則,關閉流前,緩衝區中的剩餘資料可能會丟失。

Ø public void close() throws IOException
最後當利用完流之後,應當呼叫close()方法關閉流。它會釋放所有與這個流相關的資源,如檔案控制程式碼或埠。一旦輸出流關閉了,再向其寫入資料就會觸發IOException異常。但是,有些型別可能允許對物件進行一定操作。如一個已關閉的ByteArrayOutputStream仍然可以轉化成一個實際的位元組陣列,而且一個已關閉的DigestOutputStream仍可以返回其摘要。

二、輸入流
java的基本輸入流是java.io.InputStreampublic abstract class InputStream
n public abstract int read() throws IOExceptionn public int read(byte[] data) throws IOExceptionn public int read(byte[] data,int offset,int length) throws IOExceptionn public long skip(long n) throws IOExceptionn public int available() throws IOExceptionn public void close() throws IOExceptionInputStream的具體子類使用這些方法從指定媒體讀取資料。但是不論讀取何種資源,幾乎只能使用這六種方法。有時你甚至可能不知道正在從哪種型別的流中讀取資料。如隱藏在sun.net包中TelnetInputStream是一個文件沒有說明的類。TelnetInputStream的例項由java.net包中的多種方法返回;如java.net.URL的openStram()方法。但是,這些方法僅宣告瞭返回InputStream,而不是更加明確的子類TelnetInputStream,這又是多型性在起作用了。子類的例項可以作為超類的例項透明使用。來了,又來說明方法的由來了。
Ø public abstract void read() throws IOException
InputStream類的基本方法是沒有參量的read()方法(這個與OutputStream不同了)。該方法從輸入流資源讀取一個單個位元組資料並將資料作為0到255之間的數返回,返回-1時表示流的結尾。因為Java沒有無符號位元組的資料型別,所以資料以整型型別返回。Read()方法等待和阻塞該方法後人和程式碼的執行,直到獲得資料的一個位元組並準備讀取該位元組。因此,輸入和輸出可能相當慢,這時使用者如果需要完成其他比較重要的任務時,最好試圖將I/O放到它們自己的執行緒中。Read()方法被宣告為抽象方法,因為子類需要改變它來處理特定媒體。給個例子byte[] input=new byte[10];for(int i=0;i
int b=in.read(); if(b==-1) break; input[i]=(byte)b;}
上面儘管read()方法僅讀取位元組,但是它返回的是整型值。因此在將結果儲存到位元組陣列之前,需要一個型別轉換的過程。當然,這會產生一個介於-128到127的有符號位元組,而不是read()方法返回的0到255之間的一個無符號位元組。但是,只要使用者清楚使用的是無符號還是有符號位元組就不會有很大問題。因此,我們可以把一個有符號位元組轉化成無符號位元組(轉換的原因是隻有範圍在0-255的整數才可以被儲存在java的一個byte型別的變數中)。int i=b>=0?b:256+b;這裡費了大篇幅,說明了read()返回的與java的byte型別的處理問題,大家可要注意阿,如果對java的原始資料型別還有興趣,可以看一下我的原始資料型別學習筆記(未完成)。


Ø public int read(byte[] data) throws IOException、public int read(byte[] data,int offset,int length) throws IOException
每次讀取一個位元組和每次寫入一個位元組效率都不高,因此read(byte[] data)和read(byte[] data,int offset,int length)也相應產生了。這兩個方法將從流中讀取的多個位元組填充到一個指定的陣列中。注意:這些填充到陣列的操作不一定會成功的。一個很普遍的情況是一個讀試圖不會完全失敗也不會完全成功,它可能讀出請求資料的一部分位元組,而不是全部位元組。例如,當實際上只有512位元組已經到達伺服器時,使用者可能會試圖從一個網路流上讀取1024位元組,而其他位元組仍然在傳送中,這些位元組最終會到達伺服器,但到達時卻已是不可以獲得的。因此,多位元組讀取方法會返回實際讀取的位元組數目。給個例子byte[] input=new byte[1024];int bytesRead=in.read(input);
程式碼段試圖從InputStream in讀取1024位元組到陣列input中。但是,如果僅有512位元組可以獲得,則這些位元組就是將要讀取的全部位元組,並且bytesRead值會設為512。但我們為了保證在實際上讀取到所有的位元組,怎麼辦?看int bytesRead=0;int byteToRead=1024;
byte[] input=new byte[byteToRead];
while(bytesRead
bytesRead+=in.read(input,bytesRead,byteToRead-bytesRead);}
Ø public int available() throws IOException
如果由於某種原因使用者不希望讀取資料,除非使用者想要的全部資料可

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/300209/viewspace-802030/,如需轉載,請註明出處,否則將追究法律責任。

相關文章