Java JDK6學習筆記 Stream

來源:互聯網
上載者:User

 

http://book.51cto.com/art/200812/101093.htm

 

14.2 位流

電腦中的資料都是以0與1的方式來儲存,如果要在兩個裝置之間進行資料的存取,當然也是以0與1位的方式來進行,Java將資料於目的地及來源之間的流動抽象化為一個流(Stream),而流當中流動的則是位元據。

14.2.1 InputStream和OutputStream

電腦中實際上資料的流動是通過電路,而上面流動的則是電流,電流的電位有低位與高位,即數位0與1位。從程式的觀點來說,通常會將資料目的地(例如記憶體)與來源(例如檔案)之間的資料流動抽象化為一個流(Stream),而其中流動的則是位元據,14-1所示。

 

 

在Java SE中有兩個類用來作流的抽象表示:java.io.InputStream與java.io.OutputStream。

InputStream是所有表示位輸入資料流的類之父類,它是一個抽象類別,繼承它的子類要重新定義其中所定義的抽象方法。InputStream是從裝置來源地讀取資料的抽象表示,例如System中的標準輸入資料流in對象就是一個InputStream類型的執行個體。在Java程式開始之後,in流對象就會開啟,目的是從標準輸入裝置中讀取資料,這個裝置通常是鍵盤或是使用者定義的輸入裝置。

OutputStream是所有表示位輸出資料流的類之父類,它是一個抽象類別。子類要重新定義其中所定義的抽象方法,OutputStream是用於將資料寫入目的地的抽象表示。例如System中的標準輸出資料流對象out其類型是java.io.PrintStream,這個類是OutputStream的子類(java.io.FilterOutputStream繼承OutputStream, PrintStream再繼承FilterOutputStream)。在程式開始之後,out流對象就會開啟,可以通過out來將資料寫至目的地裝置,這個裝置通常是螢幕顯示或使用者定義的輸出裝置。

範例14.4可以讀取鍵盤輸入資料流,in對象的read()方法一次讀取一個位元組的資料,讀入的資料以int類型返回。所以在使用out對象將資料顯示出來時,就是10進位方式。

範例14.4  StreamDemo.java

package onlyfun.caterpillar;

import java.io.*;

public class StreamDemo {
public static void main(String[] args) {
try {
System.out.print("輸入字元: ");
System.out.println("輸入字元十進位表示: " +
System.in.read()); 
}
catch(IOException e) {
e.printStackTrace();
}
}
}

執行結果:

輸入字元: A
輸入字元十進位表示: 65

字元A輸入後由標準輸入資料流in讀取,A的位表示以十進位來看就是65,這是A字元的編碼(查查ASCII編碼錶就知道了)。

一般來說,很少直接實現InputStream或OutputStream上的方法,因為這些方法比較低級,通常會實現它們的子類。這些子類上所定義的方法在進行輸入/輸出時更為方便。

 

 

 

14.2.2 FileInputStream和FileOutputStream

java.io.FileInputStream是InputStream的子類。從開頭File名稱上就可以知道,FileInputStream與從指定的檔案中讀取資料至目的地有關。而java.io.FileOutputStream是OutputStream的子類,顧名思義,FileOutputStream主要與從來源地寫入資料至指定的檔案中有關。

當建立一個FileInputStream或FileOutputStream的執行個體時,必須指定檔案位置及檔案名稱,執行個體被建立時檔案的流就會開啟;而不使用流時,必須關閉檔案流,以釋放與流相依的系統資源,完成檔案讀/寫的動作。

FileInputStream可以使用read()方法一次讀入一個位元組,並以int類型返回,或者是使用read()方法時讀入至一個byte數組,byte數組的元素有多少個,就讀入多少個位元組。在將整個檔案讀取完成或寫入完畢的過程中,這麼一個byte數組通常被當作緩衝區,因為這麼一個byte數組通常扮演承接資料的中間角色。

範例14.5是使用FileInputStream與FileOutputStream的一個例子。程式可以複製檔案,它會先從來源檔案讀取資料至一個byte數組中,然後再將byte數組的資料寫入目的檔案。

範例14.5  FileStreamDemo.java

package onlyfun.caterpillar;
import java.io.*;
public class FileStreamDemo {
public static void main(String[] args) {
try {
byte[] buffer = new byte[1024];

 

// 來源檔案
FileInputStream fileInputStream =
new FileInputStream(new File(args[0]));
// 目的檔案
FileOutputStream fileOutputStream =
new FileOutputStream(new File(args[1]));

 

// available()可取得未讀取的資料長度
System.out.println("複製檔案:" +
fileInputStream.available() + "位元組");

 

while(true) {
if(fileInputStream.available() < 1024) {
// 剩餘的資料比1024位元組少
// 一位一位讀出再寫入目的檔案
int remain = -1;
while((remain = fileInputStream.read())
!= -1) {
fileOutputStream.write(remain);
}
break;
}
else {
// 從來源檔案讀取資料至緩衝區
fileInputStream.read(buffer);
// 將數組資料寫入目的檔案
fileOutputStream.write(buffer);
}
}

 

// 關閉流
fileInputStream.close();
fileOutputStream.close();

 

System.out.println("複製完成");
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(
"using: java FileStreamDemo src des");
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
}
}

程式中示範了兩個read()方法,一個方法可以讀入指定長度的資料至數組,另一個方法一次可以讀入一個位元組。每次讀取之後,讀取的游標都會往前進,如果讀不到資料則返回-1,使用available()方法獲得還有多少位元組可以讀取。除了使用File來建立FileInputStream、FileOutputStream的執行個體之外,也可以直接使用字串指定路徑來建立。

// 來源檔案
FileInputStream fileInputStream =
new FileInputStream(args[0]);
// 目的檔案
FileOutputStream fileOutputStream =
new FileOutputStream(args[1]);

在不使用檔案流時,記得使用close()方法自行關閉流,以釋放與流相依的系統資源。一個執行的結果範例如下,它將FileDemo.java複製為FileDemo.txt:

java onlyfun.caterpillar.FileStreamDemo FileDemo.java FileDemo.txt
複製檔案:1723位元組
複製完成

FileOutputStream預設會以建立檔案的方式來開啟流。如果指定的檔案名稱已經存在,則原檔案會被覆蓋;如果想以附加的模式來寫入檔案,則可以在構建FileOutputStream執行個體時指定為附加模式。例如:

FileOutputStream fileOutputStream =
new FileOutputStream(args[1], true);

構建方法的第二個append參數如果設定為true,在開啟流時如果檔案不存在則會建立一個檔案,如果檔案存在就直接開啟流,並將寫入的資料附加至檔案末端。

 

 

 

14.2.3 BufferedInputStream和BufferedOutputStream

在介紹FileInputStream和FileOutputStream的例子中,使用了一個byte數組來作為資料讀入的緩衝區,以檔案存取為例,硬碟存取的速度遠低於記憶體中的資料存取速度。為了減少對硬碟的存取,通常從檔案中一次讀入一定長度的資料,而寫入時也是一次寫入一定長度的資料,這可以增加檔案存取的效率。

java.io.BufferedInputStream與java.io.BufferedOutputStream可以為InputStream、OutputStream類的對象增加緩衝區功能。構建BufferedInputStream執行個體時,需要給定一個InputStream類型的執行個體,實現BufferedInputStream時,實際上最後是實現InputStream執行個體。同樣地,在構建BufferedOutputStream時,也需要給定一個OutputStream執行個體,實現BufferedOutputStream時,實際上最後是實現OutputStream執行個體。

BufferedInputStream的資料成員buf是一個位元組,預設為2048位元組。當讀取資料來源時,例如檔案,BufferedInputStream會盡量將buf填滿。當使用read()方法時,實際上是先讀取buf中的資料,而不是直接對資料來源作讀取。當buf中的資料不足時,BufferedInputStream才會再實現給定的InputStream對象的read()方法,從指定的裝置中提取資料,14-2所示。

 

 

 

BufferedOutputStream的資料成員buf是一個位元組,預設為512位元組。當使用write()方法寫入資料時,實際上會先將資料寫至buf中,當buf已滿時才會實現給定的OutputStream對象的write()方法,將buf資料寫至目的地,而不是每次都對目的地作寫入的動作。

下面將範例14.5做個改寫,這次不用自行設定緩衝區,而使用BufferedInputStream和BufferedOutputStream讓程式看來簡單一些,也比較有效率。

範例14.6  BufferedStreamDemo.java

package onlyfun.caterpillar;

import java.io.*;

public class BufferedStreamDemo {
public static void main(String[] args) {
try {
byte[] data = new byte[1];

 

File srcFile = new File(args[0]); 
File desFile = new File(args[1]);

 

BufferedInputStream bufferedInputStream = 
new BufferedInputStream(
new FileInputStream(srcFile)); 
BufferedOutputStream bufferedOutputStream = 
new BufferedOutputStream(
new FileOutputStream(desFile));

System.out.println("複製檔案:" + 
srcFile.length() + "位元組");

 

while(bufferedInputStream.read(data) != -1) { 
bufferedOutputStream.write(data); 
}

// 將緩衝區中的資料全部寫出 
bufferedOutputStream.flush();

// 關閉流 
bufferedInputStream.close(); 
bufferedOutputStream.close();

 

System.out.println("複製完成"); 

catch(ArrayIndexOutOfBoundsException e) { 
System.out.println(
"using: java UseFileStream src des"); 
e.printStackTrace(); 

catch(IOException e) { 
e.printStackTrace(); 

}
}

為了確保緩衝區中的資料一定被寫出至目的地,建議最後執行flush()將緩衝區中的資料全部寫出目的流中。這個範例的執行結果與範例14.5是相同的。

BufferedInputStream和BufferedOutputStream並沒有改變InputStream或 OutputStream的行為,讀入或寫出時的動作還是InputStream和OutputStream負責。BufferedInputStream和BufferedOutputStream只是在操作對應的方法之前,動態地為它們加上一些額外功能(像緩衝區功能),在這裡是以檔案存取流為例,實際上可以在其他流對象上也使用BufferedInputStream和BufferedOutputStream功能。

 

14.2.4 DataInputStream和DataOutputStream

java.io.DataInputStream和java.io.DataOutputStream可提供一些對Java基礎資料型別 (Elementary Data Type)寫入的方法,像讀寫int、double和boolean等的方法。由於Java的資料類型大小是規定好的,在寫入或讀出這些基礎資料型別 (Elementary Data Type)時,就不用擔心不同平台間資料大小不同的問題。

這裡還是以檔案存取來進行說明。有時只是要儲存一個對象的成員資料,而不是整個對象的資訊,成員資料的類型假設都是Java的基礎資料型別 (Elementary Data Type),這樣的需求不必要使用到與Object輸入、輸出相關的流對象,可以使用DataInputStream、DataOutputStream來寫入或讀出資料。

下面使用範例來介紹如何使用DataInputStream與DataOutputStream。先設計一個Member類。

範例14.7  Member.java

package onlyfun.caterpillar;
public class Member {
private String name;
private int age;

 

public Member() {
}

 

public Member(String name, int age) {
this.name = name;
this.age = age;
}

 

public void setName(String name) {
this.name = name;
}

 

public void setAge(int age) {
this.age = age;
}

 

public String getName() {
return name;
}

 

public int getAge() {
return age;
}
}

打算將Member類執行個體的成員資料寫入檔案中,並打算在讀入檔案資料後,將這些資料還原為Member對象。範例14.8簡單示範了如何?這個需求。

範例14.8  DataStreamDemo.java

package onlyfun.caterpillar;

import java.io.*;

public class DataStreamDemo {
public static void main(String[] args) {
Member[] members = {new Member("Justin", 90),
new Member("momor", 95),
new Member("Bush", 88)};
try {
DataOutputStream dataOutputStream =
new DataOutputStream(
new FileOutputStream(args[0]));

for(Member member : members) {
// 寫入UTF字串
dataOutputStream.writeUTF(member.getName());
// 寫入int資料
dataOutputStream.writeInt(member.getAge());
}
// 讀出所有資料至目的地
dataOutputStream.flush();
// 關閉流
dataOutputStream.close();

DataInputStream dataInputStream =
new DataInputStream(
new FileInputStream(args[0]));
// 讀出資料並還原為對象
for(int i = 0; i < members.length; i++) {
// 讀出UTF字串
String name = dataInputStream.readUTF();
// 讀出int資料
int score = dataInputStream.readInt();
members[i] = new Member(name, score);
}
// 關閉流
dataInputStream.close();

 

// 顯示還原後的資料
for(Member member : members) { 
System.out.printf("%s/t%d%n", 
member.getName(), member.getAge());


catch(IOException e) { 
e.printStackTrace(); 

}
}

在從檔案中讀出資料時,不用費心地自行判斷讀入字串時或讀入int類型時何時該停止,使用對應的readUTF()或readInt()方法就可以正確地讀入完整類型資料。同樣地,DataInputStream、DataOutputStream並沒有改變InputStream或OutputStream的行為,讀入或寫出時的動作還是InputStream、OutputStream負責。DataInputStream、DataOutputStream只是在實現對應的方法時,動態地為它們加上類型判斷功能,在這裡雖然是以檔案存取流為例,實際上可以在其他流對象上也使用DataInputStream、DataOutputStream功能。

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.