1. 程式人生 > >Java網路程式設計(輸出流 OutputStream)

Java網路程式設計(輸出流 OutputStream)

網路程式所做的很大一部分工作都是簡單的輸入輸出:

將資料位元組從一個系統移動到另一個系統。位元組就是位元組。在很大程度上講,讀取伺服器傳送給你的資料與讀取檔案並沒有什麼不同。向客戶端傳送文字和寫檔案也沒有什麼不同。但是,Java中輸入和輸出(I/O)的組織與其他語言都不太一樣。因此,這裡說一下Java獨特的I/O方法。

Java的I/O建立在流(stream)之上。輸入流讀取檔案,輸出流寫入檔案,不同的流類,如java.io.FileInputStreamsun.net.TelnetOutputStream會讀/寫某個特定的資料來源,但是所有的輸出流都有相同的基本方法來寫入資料,所有的輸入流使用相同的基本方法來讀取資料。建立一個流之後,讀/寫時通常可以忽略讀/寫的具體細節。

過濾器流(filter)可以串鏈到輸入流或輸出流上。讀/寫資料時,過濾器可以修改資料(例如,通過加密或壓縮),或者只是提供額外的方法,將讀/寫的資料轉換為其他格式。例如,java.io.DataOutputStream類就提供了一個方法,將int轉換為4位元組,並把這些位元組寫入底層的輸入流。

閱讀器(reader)書寫器(writer)可以串鏈到輸入和輸出流上,允許程式讀/些文字(即字元) 而不是位元組。只要正確的使用,閱讀器和書寫器可以處理很多字元編碼,包括多位元組字元即SJIS和UTF-8。

流是同步的,也就是說,當程式(確切的講是執行緒)請求一個流讀/寫一段資料時,在任何其他操作時,它要等待所讀/寫的資料。Java還支援使用通道和緩衝區的非阻塞I/O。非阻塞I/O稍微有些複雜,但在某些高吞吐量的應用程式中(如web伺服器),非阻塞I/O要快的多。通常情況下,基本流模型就是實現客戶端所需要和應當使用的全部內容。由於通道和緩衝區依賴於流,下面首先介紹流和客戶端。

輸出流 Java的基本輸出流是java.io.OutputStream:


public abstract clss OutputStream

這個類提供了寫入資料所需要的基本方法。這些方法包括:

public abstract void write(int b) throws IOException


public void write(byte[] data) throws IOException


public void write(byte[] data, int offset, int length) throws IOException


public void flush() throws IOException


public void  close() throws IOException

OutputStream的子類使用這些方法向某種特定介質寫入資料。例如:FileOutputStream使用這些方法將資料寫入檔案。TelnetOutoutStream使用這些方法將資料寫入網路連線。ByteArrayOutputStream使用這些方法將資料寫入可擴充套件的字元陣列。但不管哪種介質,大多數都會使用這五種方法。有時候甚至可能不知道所寫入流的具體型別。例如,在java的類庫中找不到TelnetOutputStream。它被有意的隱藏在sun包中。java.net中很多類的很多方法都會返回TelnetOutputStream,如java.net.Socket的getOutputStream()方法。但是,這些方法宣告只返回OutputStream,而不是更特定子類TelnetOutputStream。這正是多型的威力,如果你知道如何使用這些超類,也就知道了如何使用這些子類。

OutputStream的基本方法是write(int b)。這個方法接受一個0-255之間的整數作為形參,將對應的位元組寫入到輸出流中。這個方法宣告的是抽象方法,因為各個子類需要修改這個方法來處理特定的介質。例如ByteArrayOutputStream可以用Java純程式碼實現這個方法,將位元組複製到陣列中。與此不同,FileOutputStream則需要使用原生 程式碼,這些程式碼瞭解如何將資料寫入到主機平臺的檔案中。

注意:雖然這個方法接受一個int作為引數,但實際上會寫入一個無符號位元組。Java沒有無符號位元組資料型別,所以這裡要使用int來代替。無符號位元組和有符號位元組之間的唯一區別在於解釋。它們都是由8個二進位制組成,當使用write(int b)將int寫入一個網路連線時線纜上只會放8個二進位制位組。如果將一個超出0-255的int傳入write(int b),將寫入這個數的最低位元組,其他3個位元組將會被忽略。

與在網路硬體中的快取一樣,流還可以在軟體中得到快取。一般說來,這可以通過BufferedOutputStream或BufferedWriter串鏈到底層流上來實現,因此在寫入資料完成後,重新整理(flush)輸出流十分重要。例如,假設已經向使用HttpKeep-Alive的HTTP1.1伺服器寫入了300位元組的請求,通常會等待響應,然後再發送更多的資料。不過,如果輸出流有一個1024位元組的緩衝區,那麼這個流在傳送緩衝區中資料之前會等待更多的資料到達。在伺服器到達之前不會向流寫入更多的資料,但是響應永遠不會到來,因為請求還沒有傳送!flush()方法可以強迫緩衝的流傳送資料,即使緩衝區還沒有滿,以此來打破這種死鎖的狀態。

不管你是否覺得有必要,重新整理輸出流都很重要。取決於你如何控制流的引用,可能知道流是否緩衝也可能不知道(例如,不論你是否希望如此,System.out都會緩衝)。如果重新整理某個流對於某個特定的流來說沒有必要,那麼它也只是個低成本的操作。不過,如果有必要重新整理輸出,就必須完成這個操作。需要重新整理輸出時如果未能完成這個操作,那麼會導致不可預知, 不可重現的程式掛起,如果你未能清楚的知道問題出在哪裡,那麼診斷起來會非常的困難。相應地,應當在關閉流之前 重新整理輸出所有的流。否則,關閉流時留在緩衝區中的資料可能會丟失。

最後,當結束一個流的操作時,要通過呼叫它的close()方法將其關閉。這會釋放與這個流關聯的所有資源,如檔案控制代碼或埠。如果流來自一個網路連線,那麼關閉流也會終止這個連線。一旦輸出流關閉,繼續寫入時就會丟擲IOException異常。不過,有些流仍允許對這個物件做一些處理。例如,關閉的ByteArrayOutputStream仍然可以轉換為實際的位元組陣列,關閉的DigestOutputStream仍然可以返回摘要。

一個檔案複製的例子:

package com.hello.java04;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

public class FileCopy {

      public static void main(String[] args) {

            

            byte[] buffer=new byte[1024];

            int numberRead = 0;

            FileInputStream input = null;

            FileOutputStream output = null;

            try {

                  input = new FileInputStream("E:/text/1.txt");

                  

                  output = new FileOutputStream("E:/text/2.txt");

                  try {

                        while((numberRead=input.read(buffer))!=-1 ){

                              

                              output.write(buffer, 0, numberRead);

                        }

                        

                  } catch (IOException e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                  }

            } catch (FileNotFoundException e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

            }

            finally{

                  try {

                        input.close();

                        output.close();

                  } catch (IOException e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                  }

                  

            }

            

            

      }

}