java中的高級流

java.io定義的輸入/輸出類列於下表:html

 

 

ObjectInputStream.GetField和ObjectOutputStream.PutField是Java2新添的內部類。java

java.io包還包含兩個不受java2歡迎的類,這兩個類沒有在上表中列出:LineNumberInputStream和StringBufferInputStream。新代碼不該該使用兩個類。程序員

下面是由java.io定義的接口:編程

 

FileFilter接口是Java2新增的。數組

java.io包中有不少類和接口。包括字節和字符流,對象序列化(對象的存儲和釋放)。安全

本章講述幾個最經常使用的I/O成員,從最獨特的File開始。性能優化

File(文件類)網絡

 

 

儘管java.io定義的大多數類是實行流式操做的,File類不是。它直接處理文件和文件系統。也就是說,File類沒有指定信息怎樣從文件讀取或向文件存儲;它描述了文件自己的屬性。File對象用來獲取或處理與磁盤文件相關的信息,例如權限,時間,日期和目錄路徑。此外,File還瀏覽子目錄層次結構。不少程序中文件是數據的根源和目標。儘管它們在小應用程序中由於安全緣由而受到嚴格限制,文件還是存儲固定和共享信息的主要資源。Java中的目錄當成File對待,它具備附加的屬性——一個能夠被list()方法檢測的文件名列表。併發

下面的構造函數能夠用來生成File對象:app

File(StringdirectoryPath)

File(StringdirectoryPath,Stringfilename)

File(FiledirObj,Stringfilename)

這裏,directoryPath是文件的路徑名,filename是文件名,dirObj一個指定目錄的File對象。

下面的例子建立了三個文件:f1,f2,和f3。第一個File對象是由僅有一個目錄路徑參數的構造函數生成的。第二個對象有兩個參數——路徑和文件名。第三個File對象的參數包括指向f1文件的路徑及文件名。f3和f2指向相同的文件。

 

Filef1=newFile("/");

Filef2=newFile("/","autoexec.bat");

Filef3=newFile(f1,"autoexec.bat");

注意:Java能正確處理UNIX和Windows/DOS約定路徑分隔符。若是在Windows版本的Java下用斜線(/),路徑處理依然正確。記住,若是你用Windows/DOS使用反斜線(\)的約定,你須要在字符串內使用它的轉義序列(\\)。Java約定是用UNIX和URL風格的斜線來做路徑分隔符。

File定義了不少獲取File對象標準屬性的方法。例如getName()返回文件名,getParent()返回父目錄名,exists()在文件存在的狀況下返回true,反之返回false。然而File類是不對稱的。說它不對稱,意思是雖然存在容許驗證一個簡單文件對象屬性的不少方法,可是沒有相應的函數來改變這些屬性。下面的例子說明了幾個File方法:

//DemonstrateFile.

importjava.io.File;

 

classFileDemo{

staticvoidp(Strings){

System.out.println(s);

}

 

publicstaticvoidmain(Stringargs[]){

Filef1=newFile("/java/COPYRIGHT");

p("FileName:"+f1.getName());

p("Path:"+f1.getPath());

p("AbsPath:"+f1.getAbsolutePath());

p("Parent:"+f1.getParent());

p(f1.exists()?"exists":"doesnotexist");

p(f1.canWrite()?"iswriteable":"isnotwriteable");

p(f1.canRead()?"isreadable":"isnotreadable");

p("is"+(f1.isDirectory()?"":"not"+"adirectory"));

p(f1.isFile()?"isnormalfile":"mightbeanamedpipe");

p(f1.isAbsolute()?"isabsolute":"isnotabsolute");

p("Filelastmodified:"+f1.lastModified());

p("Filesize:"+f1.length()+"Bytes");

}

}

運行該程序,你將看到下面的結果:

FileName:COPYRIGHT

Path:/java/COPYRIGHT

AbsPath:/java/COPYRIGHT

Parent:/java

exists

iswriteable

isreadable

isnotadirectory

isnormalfile

isabsolute

Filelastmodified:812465204000

Filesize:695Bytes

大多數File方法是自說明的,但isFile()和isAbsolute()不是。isFile()在被文件調用時返回true,在被目錄調用時返回false。而且,isFile()被一些專用文件調用時返回false,例如設備驅動程序和命名管道,因此該方法可用來斷定文件是否做爲文件執行。isAbsolute()方法在文件擁有絕對路徑時返回true,如果相對路徑則返回false。

File還包括兩個有用的實用工具方法。第一個是renameTo(),顯示以下:

booleanrenameTo(FilenewName)

這裏,由newName指定的文件名變成了所調用的File對象的新的名稱。若是改名成功則返回ture,文件不能被重命名(例如,你試圖重命名文件以使它從一個目錄轉到另外一個目錄,或者你使用了一個已經存在的文件名),則返回false。

第二個實用工具方法是delete(),該方法刪除由被調用的File對象的路徑指定的磁盤文件。它的形式以下:

booleandelete()

一樣能夠在目錄爲空時用delete()刪除目錄。若是刪除了文件,delete()返回true,若是文件不能被刪除則返回false。

Java2爲File類增添了一些新的方法,你會發如今某些場合這些新增方法頗有用。一些最有趣的方法顯示以下:

 

而且,由於File類如今支持Comparable接口,compareTo()方法也被支持。

目錄操做

 

 

目錄是一個包含其餘文件和路徑列表的File類。當你建立一個File對象且它是目錄時,isDirectory()方法返回ture。這種狀況下,能夠調用該對象的list()方法來提取該目錄內部其餘文件和目錄的列表。該方法有兩種形式。第一種形式以下:

String[]list()

文件列表在一個String對象數組中返回。

下面顯示的程序說明怎樣用list()來檢查一個目錄的內容:

//Usingdirectories.

importjava.io.File;

 

classDirList{

publicstaticvoidmain(Stringargs[]){

Stringdirname="/java";

Filef1=newFile(dirname);

 

if(f1.isDirectory()){

System.out.println("Directoryof"+dirname);

Strings[]=f1.list();

 

for(inti=0;i<s.length;i++){

Filef=newFile(dirname+"/"+s[i]);

if(f.isDirectory()){

System.out.println(s[i]+"isadirectory");

}else{

System.out.println(s[i]+"isafile");

}

}

}else{

System.out.println(dirname+"isnotadirectory");

}

}

}

下面是程序的樣本輸出(固然,目錄下的內容不一樣,輸出也不一樣):

Directoryof/java

binisadirectory

libisadirectory

demoisadirectory

COPYRIGHTisafile

READMEisafile

index.htmlisafile

includeisadirectory

src.zipisafile

.hotjavaisadirectory

Srcisadirectory

 

使用FilenameFilter

 

你老是但願可以限制由list()方法返回的文件數目,使它僅返回那些與必定的文件名方式或者過濾(filter)相匹配的文件。爲達到這樣的目的,必須使用list()的第二種形式:

String[]list(FilenameFilterFFObj)

該形式中,FFObj是一個實現FilenameFilter接口的類的對象。

FilenameFilter僅定義了一個方法,accept()。該方法被列表中的每一個文件調用一次。它的一般形式以下:

booleanaccept(Filedirectory,Stringfilename)

當被directory指定的目錄中的文件(也就是說,那些與filename參數匹配的文件)包含在列表中時,accept()方法返回true,當這些文件沒有包括在列表中時,accept()返回false。

下面顯示的OnlyExt類實現FilenameFilter接口,它被用來修飾前面的程序,限制由list()返回的文件名的可見度,把對象被建立時以指定擴展名結束的文件歸檔。

importjava.io.*;

 

publicclassOnlyExtimplementsFilenameFilter{

Stringext;

 

publicOnlyExt(Stringext){

this.ext="."+ext;

}

 

publicbooleanaccept(Filedir,Stringname){

returnname.endsWith(ext);

}

}

修改過的目錄列表程序顯示以下。如今它只顯示以.html爲擴展名的文件。

//Directoryof.HTMLfiles.

importjava.io.*;

 

classDirListOnly{

publicstaticvoidmain(Stringargs[]){

Stringdirname="/java";

Filef1=newFile(dirname);

FilenameFilteronly=newOnlyExt("html");

Strings[]=f1.list(only);

 

for(inti=0;i<s.length;i++){

System.out.println(s[i]);

}

}

}

listFiles()方法

 

Java2增長了list()方法的一個變化形式,名爲listFiles(),你會發現該方法頗有用。

listFiles()形式以下:

File[]listFiles()

File[]listFiles(FilenameFilterFFObj)

File[]listFiles(FileFilterFObj)

上述三種形式以File對象矩陣的形式返回文件列表,而不是用字符串形式返回。第一種形式返回全部的文件,第二種形式返回知足指定FilenameFilter接口的文件。除了返回一個File對象數組,這兩個listFiles()方法就像list()方法同樣工做。

第三種listFiles()形式返回知足指定FileFilter的路徑名的文件。FileFilter只定義了一個accept()方法,該方法被列表中的每一個文件調用一次。它的一般形式以下:

booleanaccept(Filepath)

若是文件被包括在列表中(即與path參數匹配的文件),accept()方法返回true,若是不被包括,則返回false。

建立目錄

 

另外兩個有用的File類的方法是mkdir()和mkdirs()。mkdir()方法建立了一個目錄,建立成功返回true,建立失敗返回false。建立失敗是指File對象指定的目錄已經存在,或者是由於整個路徑不存在而不能建立目錄。建立路徑不存在的目錄,用mkdirs()的方法。它建立目錄以及該目錄全部的父目錄。

Java輸入/輸出類和接口

 

 

Java的流式輸入/輸出創建在四個抽象類的基礎上:InputStream,OutputStream,Reader和Writer。這些類在第12章中有過簡要的討論。它們用來建立具體流式子類。儘管程序經過具體子類執行輸入/輸出操做,頂層的類定義了全部流類的基礎通用功能。

InputStream和OutputStream設計成字節流類。Reader和Writer爲字符流設計。字節流類和字符流類造成分離的層次結構。通常說來,處理字符或字符串時應使用字符流類,處理字節或二進制對象時應用字節流類。

下面分別講述字節流和字符流類。

字節流

 

字節流類爲處理字節式輸入/輸出提供了豐富的環境。一個字節流能夠和其餘任何類型的對象並用,包括二進制數據。這樣的多功能性使得字節流對不少類型的程序都很重要。

由於字節流類以InputStream和OutputStream爲頂層,咱們就從討論這兩個類開始。

InputStream(輸入流)

 

InputStream是一個定義了Java流式字節輸入模式的抽象類。該類的全部方法在出錯條件下引起一個IOException異常。表10-1顯示了InputStream的方法。

 

OutputStream是定義了流式字節輸出模式的抽象類。該類的全部方法返回一個void值而且在出錯狀況下引起一個IOException異常。表10-2顯示了OutputStream的方法。

 

 

注意:多數在表17-1和表17-2中描述的方法由InputStream和OutputStream的子類實現,但mark()和reset()方法除外。注意下面討論的每一個子類中它們的使用和不用狀況。

FileInputStream(文件輸入流)

 

FileInputStream類建立一個能從文件讀取字節的InputStream類,它的兩個經常使用的構造函數以下:

FileInputStream(Stringfilepath)

FileInputStream(FilefileObj)

它們都能引起FileNotFoundException異常。這裏,filepath是文件的全稱路徑,fileObj是描述該文件的File對象。

下面的例子建立了兩個使用一樣磁盤文件且各含一個上述構造函數的FileInputStreams類:

FileInputStreamf0=newFileInputStream("/autoexec.bat")

Filef=newFile("/autoexec.bat");

FileInputStreamf1=newFileInputStream(f);

儘管第一個構造函數可能更經常使用到,第二個構造函數容許在把文件賦給輸入流以前用File方法更進一步檢查文件。當一個FileInputStream被建立時,它能夠被公開讀取。

FileInputStream重載了抽象類InputStream的六個方法,mark()和reset()方法不被重載,任何關於使用FileInputStream的reset()嘗試都會生成IOException異常。

下面的例題說明了怎樣讀取單個字節、字節數組以及字節數組的子界。它一樣闡述了怎樣運用available()斷定剩餘的字節個數及怎樣用skip()方法跳過沒必要要的字節。該程序讀取它本身的源文件,該源文件一定在當前目錄中。

//DemonstrateFileInputStream.

importjava.io.*;

 

classFileInputStreamDemo{

publicstaticvoidmain(Stringargs[])throwsException{

intsize;

InputStreamf=

newFileInputStream("FileInputStreamDemo.java");

 

System.out.println("TotalAvailableBytes:"+

(size=f.available()));

intn=size/40;

System.out.println("First"+n+

"bytesofthefileoneread()atatime");

for(inti=0;i<n;i++){

System.out.print((char)f.read());

}

System.out.println("\nStillAvailable:"+f.available());

System.out.println("Readingthenext"+n+

"withoneread(b[])");

byteb[]=newbyte[n];

if(f.read(b)!=n){

System.err.println("couldn'tread"+n+"bytes.");

}

System.out.println(newString(b,0,n));

System.out.println("\nStillAvailable:"+(size=f.available()));

System.out.println("Skippinghalfofremainingbyteswithskip()");

f.skip(size/2);

System.out.println("StillAvailable:"+f.available());

System.out.println("Reading"+n/2+"intotheendofarray");

if(f.read(b,n/2,n/2)!=n/2){

System.err.println("couldn'tread"+n/2+"bytes.");

}

System.out.println(newString(b,0,b.length));

System.out.println("\nStillAvailable:"+f.available());

f.close();

}

}

下面是該程序的輸出:

TotalAvailableBytes:1433

First35bytesofthefileoneread()atatime

//DemonstrateFileInputStream.

im

StillAvailable:1398

Readingthenext35withoneread(b[])

portjava.io.*;

 

classFileInputS

 

StillAvailable:1363

Skippinghalfofremainingbyteswithskip()

StillAvailable:682

Reading17intotheendofarray

portjava.io.*;

read(b)!=n){

S

 

StillAvailable:665

這個有些刻意創做的例子說明了怎樣讀取數據的三種方法,怎樣跳過輸入以及怎樣檢查流中能夠得到數據的數目。

FileOutputStream(文件輸出流)

 

FileOutputStream建立了一個能夠向文件寫入字節的類OutputStream,它經常使用的構造函數以下:

FileOutputStream(StringfilePath)

FileOutputStream(FilefileObj)

FileOutputStream(StringfilePath,booleanappend)

它們能夠引起IOException或SecurityException異常。這裏filePath是文件的全稱路徑,fileObj是描述該文件的File對象。若是append爲true,文件以設置搜索路徑模式打開。

FileOutputStream的建立不依賴於文件是否存在。在建立對象時FileOutputStream在打開輸出文件以前建立它。這種狀況下你試圖打開一個只讀文件,會引起一個IOException異常。

下面的例子建立一個樣本字節緩衝器。先生成一個String對象,接着用getBytes()方法提取字節數組對等體。而後建立了三個文件。第一個file1.txt將包括樣本中的各個字節。第二個文件是file2.txt,它包括全部字節。第三個也是最後一個文件file3.txt,僅包含最後的四分之一。不像FileInputStream類的方法,全部FileOutputStream類的方法都返回一個void類型值。在出錯狀況下,這些方法將引起IOException異常。

//DemonstrateFileOutputStream.

importjava.io.*;

 

classFileOutputStreamDemo{

publicstaticvoidmain(Stringargs[])throwsException{

Stringsource="Nowisthetimeforallgoodmen\n"

+"tocometotheaidoftheircountry\n"

+"andpaytheirduetaxes.";

bytebuf[]=source.getBytes();

OutputStreamf0=newFileOutputStream("file1.txt");

for(inti=0;i<buf.length;i+=2){

f0.write(buf[i]);

}

f0.close();

 

OutputStreamf1=newFileOutputStream("file2.txt");

f1.write(buf);

f1.close();

 

OutputStreamf2=newFileOutputStream("file3.txt");

f2.write(buf,buf.length-buf.length/4,buf.length/4);

f2.close();

}

}

下面是運行該程序以後,每一個文件的內容,首先是file1.txt:

Nwihiefralgoetoethiftercutynahiuae.

接着,是file2.txt:

Nowisthetimeforallgoodmen

tocometotheaidoftheircountry

andpaytheirduetaxes.

最後,file3.txt

ndpaytheirduetaxes.

 

ByteArrayInputStream(字節數組輸入流)

 

ByteArrayInputStream是把字節數組當成源的輸入流。該類有兩個構造函數,每一個構造函數須要一個字節數組提供數據源:

ByteArrayInputStream(bytearray[])

ByteArrayInputStream(bytearray[],intstart,intnumBytes)

這裏,array是輸入源。第二個構造函數建立了一個InputStream類,該類從字節數組的子集生成,以start指定索引的字符爲起點,長度由numBytes決定。

下面的例子建立了兩個ByteArrayInputStream,用字母表的字節表示初始化它們:

//DemonstrateByteArrayInputStream.

importjava.io.*;

 

classByteArrayInputStreamDemo{

publicstaticvoidmain(Stringargs[])throwsIOException{

Stringtmp="abcdefghijklmnopqrstuvwxyz";

byteb[]=tmp.getBytes();

ByteArrayInputStreaminput1=newByteArrayInputStream(b);

ByteArrayInputStreaminput2=newByteArrayInputStream(b,0,3);

}

}

input1對象包含整個字母表中小寫字母,input2僅包含開始的三個字母。

ByteArrayInputStream實現mark()和reset()方法。然而,若是mark()不被調用,reset()在流的開始設置流指針——該指針是傳遞給構造函數的字節數組的首地址。下面的例子說明了怎樣用reset()方法兩次讀取一樣的輸入。這種狀況下,咱們讀取數據,而後分別用小寫和大寫字母打印「abc」。

importjava.io.*;

 

classByteArrayInputStreamReset{

publicstaticvoidmain(Stringargs[])throwsIOException{

Stringtmp="abc";

byteb[]=tmp.getBytes();

ByteArrayInputStreamin=newByteArrayInputStream(b);

 

for(inti=0;i<2;i++){

intc;

while((c=in.read())!=-1){

if(i==0){

System.out.print((char)c);

}else{

System.out.print(Character.toUpperCase((char)c));

}

}

System.out.println();

in.reset();

}

}

}

該例先從流中讀取每一個字符,而後以小寫字母形式打印。而後從新設置流並從頭讀起,此次在打印以前先將字母轉換成大寫字母。下面是輸出:

abc

ABC

ByteArrayOutputStream(字節數組輸出流)

 

ByteArrayOutputStream是一個把字節數組看成輸出流的實現。ByteArrayOutputStream有兩個構造函數,以下:

ByteArrayOutputStream()

ByteArrayOutputStream(intnumBytes)

在第一種形式裏,一個32位字節的緩衝器被生成。第二個構造函數生成一個跟指定numBytes相同位數的緩衝器。緩衝器保存在ByteArrayOutputStream的受保護的buf成員裏。緩衝器的大小在須要的狀況下會自動增長。緩衝器保存的字節數是由ByteArrayOutputStream的受保護的count域保存的。

下面的例子說明了ByteArrayOutputStream:

//DemonstrateByteArrayOutputStream.

importjava.io.*;

 

classByteArrayOutputStreamDemo{

publicstaticvoidmain(Stringargs[])throwsIOException{

ByteArrayOutputStreamf=newByteArrayOutputStream();

Strings="Thisshouldendupinthearray";

bytebuf[]=s.getBytes();

 

f.write(buf);

System.out.println("Bufferasastring");

System.out.println(f.toString());

System.out.println("Intoarray");

byteb[]=f.toByteArray();

for(inti=0;i<b.length;i++){

System.out.print((char)b[i]);

}

System.out.println("\nToanOutputStream()");

OutputStreamf2=newFileOutputStream("test.txt");

 

f.writeTo(f2);

f2.close();

System.out.println("Doingareset");

f.reset();

for(inti=0;i<3;i++)

f.write('X');

System.out.println(f.toString());

}

}

運行程序後,生成下面的輸出。注意在調用reset()以後,三個X怎樣結束。

Bufferasastring

Thisshouldendupinthearray

Intoarray

Thisshouldendupinthearray

ToanOutputStream()

Doingareset

XXX

該例用writeTo()這一便捷的方法將f的內容寫入test.txt,檢查在前面例子中生成的test.txt文件內容,結果以下:

Thisshouldendupinthearray

 

過濾字節流

 

過濾流(filteredstream)僅僅是底層透明地提供擴展功能的輸入流(輸出流)的包裝。

這些流通常由普通類的方法(即過濾流的一個超類)訪問。典型的擴展是緩衝,字符轉換和原始數據轉換。這些過濾字節流是FilterInputStream和FilterOutputStream。它們的構造函數以下:

FilterOutputStream(OutputStreamos)

FilterInputStream(InputStreamis)

這些類提供的方法和InputStream及OutputStream類的方法相同。

緩衝字節流

 

對於字節流,緩衝流(bufferedstream),經過把內存緩衝器連到輸入/輸出流擴展一個過濾流類。該緩衝器容許Java對多個字節同時進行輸入/輸出操做,提升了程序性能。由於緩衝器可用,因此能夠跳過、標記和從新設置流。緩衝字節流類是BufferedInputStream和BufferedOutputStream。PushbackInputStream也可實現緩衝流。

BufferedInputStream(緩衝輸入流)

緩衝輸入/輸出是一個很是普通的性能優化。Java的BufferedInputStream類容許把任何InputStream類「包裝」成緩衝流並使它的性能提升。

 

BufferedInputStream有兩個構造函數:

BufferedInputStream(InputStreaminputStream)

BufferedInputStream(InputStreaminputStream,intbufSize)

第一種形式生成了一個默認緩衝長度的緩衝流。第二種形式緩衝器大小是由bufSize傳入的。使用內存頁或磁盤塊等的若干倍的緩衝區大小能夠給執行性能帶來很大的正面影響。但這是依賴於執行狀況的。最理想的緩衝長度通常與主機操做系統、可用內存空間及機器配置有關。合理利用緩衝不須要特別複雜的操做。通常緩衝大小爲8192個字節,給輸入/輸出流設定一個更小的緩衝器一般是好的方法。用這樣的方法,低級系統能夠從磁盤或網絡讀取數據塊並在緩衝器中存儲結果。所以,即便你在InputStream外同時讀取字節數據時,也能夠在超過99.9%的時間裏得到快速存儲操做。

緩衝一個輸入流一樣提供了在可用緩衝器的流內支持向後移動的必備基礎。除了在任何InputStream類中執行的read()和skip()方法外,BufferedInputStream一樣支持mark()和reset()方法。BufferedInputStream.markSupported()返回true是這一支持的體現。

下面的例子設計了一種情形,該情形下,咱們可使用mark()來記憶咱們在輸入流中的位置,而後用reset()方法返回該位置。這個例子分析了HTML實體的引用爲版權信息的狀況。這個引用以一個(&)符號開始以分號(;)結束,沒有任何空格。例子輸入由兩個&符號來講明何處reset()發生,何處不發生的狀況。

//Usebufferedinput.

importjava.io.*;

 

classBufferedInputStreamDemo{

publicstaticvoidmain(Stringargs[])throwsIOException{

Strings="Thisisa&copy;copyrightsymbol"+

"butthisis&copynot.\n";

bytebuf[]=s.getBytes();

ByteArrayInputStreamin=newByteArrayInputStream(buf);

BufferedInputStreamf=newBufferedInputStream(in);

intc;

booleanmarked=false;

 

while((c=f.read())!=-1){

switch(c){

case'&':

if(!marked){

f.mark(32);

marked=true;

}else{

marked=false;

}

break;

case';':

if(marked){

marked=false;

System.out.print("(c)");

}else

System.out.print((char)c);

break;

case'':

if(marked){

marked=false;

f.reset();

System.out.print("&");

}else

System.out.print((char)c);

break;

default:

if(!marked)

System.out.print((char)c);

break;

}

}

}

}

注意該例運用mark(32),該方法保存接下來所讀取的32個字節(這個數量對全部的實體引用都足夠)。下面是程序的輸出:

Thisisa(c)copyrightsymbolbutthisis&copynot.

警告:在緩衝器中使用mark()是受限的。意思是說你只能給mark()定義一個小於流緩衝大小的參數。

BufferedOutputStream(緩衝輸出流)

BufferedOutputStream與任何一個OutputStream相同,除了用一個另外的flush()方法來保證數據緩衝器被寫入到實際的輸出設備。由於BufferedOutputStream是經過減少系統寫數據的時間而提升性能的,能夠調用flush()方法生成緩衝器中待寫的數據。不像緩衝輸入,緩衝輸出不提供額外的功能,Java中輸出緩衝器是爲了提升性能的。

下面是兩個可用的構造函數:

BufferedOutputStream(OutputStreamoutputStream)

BufferedOutputStream(OutputStreamoutputStream,intbufSize)

第一種形式建立了一個使用512字節緩衝器的緩衝流。第二種形式,緩衝器的大小由bufSize參數傳入。

PushbackInputStream(推回輸入流)

緩衝的一個新穎的用法是實現推回(pushback)。Pushback用於輸入流容許字節被讀取而後返回(即「推回」)到流。PushbackInputStream類實現了這個想法。它提供了一種機制來「窺視」在沒有受到破壞的狀況下輸入流生成了什麼。

PushbackInputStream有兩個構造函數:

PushbackInputStream(InputStreaminputStream)

PushbackInputStream(InputStreaminputStream,intnumBytes)

第一種形式建立了一個容許一個字節推回到輸入流的流對象。第二種形式建立了一個具備numBytes長度緩衝區的推回緩衝流。它容許多個字節推回到輸入流。

除了具備與InputStream相同的方法,PushbackInputStream提供了unread()方法,表示以下:

voidunread(intch)

voidunread(bytebuffer[])

voidunread(bytebuffer,intoffset,intnumChars)

第一種形式推回ch的低位字節,它將是隨後調用read()方法所返回的下一個字節。第二種形式返回buffer緩衝器中的字節。第三種形式推回buffer中從offset處開始的numChars個字節。若是在推回緩衝器爲滿時試圖返回一個字節,IOException異常將被引起。

Java2對PushbackInputStream做了一些小的修改:它實現skip()方法。

下面的例子演示一個編程語言解析器怎樣用PushbackInputStream和unread()來處理==操做符和=操做符之間的不一樣的。

//Demonstrateunread().

importjava.io.*;

 

classPushbackInputStreamDemo{

publicstaticvoidmain(Stringargs[])throwsIOException{

Strings="if(a==4)a=0;\n";

bytebuf[]=s.getBytes();

ByteArrayInputStreamin=newByteArrayInputStream(buf);

PushbackInputStreamf=newPushbackInputStream(in);

intc;

 

while((c=f.read())!=-1){

switch(c){

case'=':

if((c=f.read())=='=')

System.out.print(".eq.");

else{

System.out.print("<-");

f.unread(c);

}

break;

default:

System.out.print((char)c);

break;

}

}

}

}

下面是例子程序的輸出。注意==被「.eq」代替而=被「<-」代替。

if(a.eq.4)a<-0;

注意:PushbackInputStream具備使InputStream生成的mark()或reset()方法失效的反作用。用markSupported()來檢查你運用mark()/reset()的任何流類。

SequenceInputStream(順序輸入流)

 

SequenceInputStream類容許鏈接多個InputStream流。SequenceInputStream的構造不一樣於任何其餘的InputStream。SequenceInputStream構造函數要麼使用一對InputStream,要麼用InputStream的一個Enumeration,顯示以下:

SequenceInputStream(InputStreamfirst,InputStreamsecond)

SequenceInputStream(EnumerationstreamEnum)

操做上來講,該類知足讀取完第一個InputStream後轉去讀取第二個流的讀取要求。使用Enumeration的狀況下,它將繼續讀取全部InputStream流直到最後一個被讀完。

下面是用SequenceInputStream輸出兩個文件內容的例子程序:

//Demonstratesequencedinput.

importjava.io.*;

importjava.util.*;

 

classInputStreamEnumeratorimplementsEnumeration{

privateEnumerationfiles;

publicInputStreamEnumerator(Vectorfiles){

this.files=files.elements();

}

 

publicbooleanhasMoreElements(){

returnfiles.hasMoreElements();

}

 

publicObjectnextElement(){

try{

returnnewFileInputStream(files.nextElement().toString());

}catch(Exceptione){

returnnull;

}

}

}

 

classSequenceInputStreamDemo{

publicstaticvoidmain(Stringargs[])throwsException{

intc;

Vectorfiles=newVector();

 

files.addElement("/autoexec.bat");

files.addElement("/config.sys");

InputStreamEnumeratore=newInputStreamEnumerator(files);

InputStreaminput=newSequenceInputStream(e);

 

while((c=input.read())!=-1){

System.out.print((char)c);

}

input.close();

}

}

該例建立了一個Vector向量並向它添加了兩個文件名。它把名字向量傳給

InputStreamEnumerator類,設計該類是爲了提供向量包裝器,向量返回的元素不是文件名,而是用這些名稱打開FileInputStream流。SequenceInputStream依次打開每一個文件,該程序打印了兩個文件的內容。

PrintStream(打印流)

 

PrintStream具備本書開始以來咱們在System文件句柄使用過的System.out全部的格式化性能。PrintStream有兩個構造函數:

PrintStream(OutputStreamoutputStream)

PrintStream(OutputStreamoutputStream,booleanflushOnNewline)

當flushOnNewline控制Java每次刷新輸出流時,輸出一個換行符(\n)。若是flushOnNewline爲true,自動刷新。若爲false,刷新不能自動進行。第一個構造函數不支持自動刷新。

Java的PrintStream對象支持包括Object在內的各類類型的print()和println()方法。若是參數不是一個簡單類型,PrintStream方法將調用對象的toString()方法,而後打印結果。

RandomAccessFile(隨機訪問文件類)

RandomAccessFile包裝了一個隨機訪問的文件。它不是派生於InputStream和OutputStream,而是實現定義了基本輸入/輸出方法的DataInput和DataOutput接口。它一樣支持定位請求——也就是說,能夠在文件內部放置文件指針。它有兩個構造函數:

RandomAccessFile(FilefileObj,Stringaccess)

throwsFileNotFoundException

RandomAccessFile(Stringfilename,Stringaccess)

throwsFileNotFoundException

第一種形式,fileObj指定了做爲File對象打開的文件的名稱。第二種形式,文件名是由filename參數傳入的。兩種狀況下,access都決定容許訪問何種文件類型。若是是「r」,那麼文件可讀不可寫,若是是「rw」,文件以讀寫模式打開。

下面所示的seek()方法,用來設置文件內部文件指針的當前位置:

voidseek(longnewPos)throwsIOException

這裏,newPos指文件指針從文件開始以字節方式指定新位置。調用seek()方法後,接下來的讀或寫操做將在文件的新位置發生。

RandomAccessFile實現了用來讀寫隨機訪問文件的標準的輸入和輸出方法。下面是Java2增添的新方法setLength()。它有下面的形式:

voidsetLength(longlen)throwsIOException

該方法經過指定的len設置正在調用的文件的長度。該方法能夠增加或縮短一個文件。

若是文件被加長,增長的部分是未定義的。

字符流

 

 

儘管字節流提供了處理任何類型輸入/輸出操做的足夠的功能,它們不能直接操做Unicode字符。既然Java的一個主要目的是支持「只寫一次,處處運行」的哲學,包括直接第17章輸入/輸出:探究java.jo383的字符輸入/輸出支持是必要的。本節將討論幾個字符輸入/輸出類。如前所述,字符流層次結構的頂層是Reader和Writer抽象類。咱們將從它們開始。

注意:如第12章討論過的,字符輸入/輸出類是在java的1.1版本中新加的。由此,你仍然能夠發現遺留下的程序代碼在應該使用字符流時卻使用了字節流。當遇到這種代碼,最好更新它。

Reader

 

Reader是定義Java的流式字符輸入模式的抽象類。該類的全部方法在出錯狀況下都將引起IOException異常。表10-3給出了Reader類中的方法。

 

 

Writer

 

Writer是定義流式字符輸出的抽象類。全部該類的方法都返回一個void值並在出錯條件下引起IOException異常。表10-4給出了Writer類中方法。

 

 

 

FileReader類建立了一個能夠讀取文件內容的Reader類。它最經常使用的構造函數顯示以下:

FileReader(StringfilePath)

FileReader(FilefileObj)

每個都能引起一個FileNotFoundException異常。這裏,filePath是一個文件的完整路徑,fileObj是描述該文件的File對象。

下面的例子演示了怎樣從一個文件逐行讀取並把它輸出到標準輸入流。例子讀它本身的源文件,該文件必定在當前目錄。

//DemonstrateFileReader.

importjava.io.*;

 

classFileReaderDemo{

publicstaticvoidmain(Stringargs[])throwsException{

FileReaderfr=newFileReader("FileReaderDemo.java");

BufferedReaderbr=newBufferedReader(fr);

Strings;

 

while((s=br.readLine())!=null){

System.out.println(s);

}

 

fr.close();

}

}

FileWriter

 

FileWriter建立一個能夠寫文件的Writer類。它最經常使用的構造函數以下:

FileWriter(StringfilePath)

FileWriter(StringfilePath,booleanappend)

FileWriter(FilefileObj)

它們能夠引起IOException或SecurityException異常。這裏,filePath是文件的徹底路徑,fileObj是描述該文件的File對象。若是append爲true,輸出是附加到文件尾的。

FileWriter類的建立不依賴於文件存在與否。在建立文件以前,FileWriter將在建立對象時打開它來做爲輸出。若是你試圖打開一個只讀文件,將引起一個IOException異常。

下面的例子是前面討論FileOutputStream時用到例子的字符流形式的版本。它建立了一個樣本字符緩衝器,開始生成一個String,而後用getChars()方法提取字符數組。而後該例建立了三個文件。第一個file1.txt,包含例子中的隔個字符。第二個file2.txt,包含全部的字符。最後,第三個文件file3.txt,只含有最後的四分之一。

//DemonstrateFileWriter.

importjava.io.*;

 

classFileWriterDemo{

publicstaticvoidmain(Stringargs[])throwsException{

Stringsource="Nowisthetimeforallgoodmen\n"

+"tocometotheaidoftheircountry\n"

+"andpaytheirduetaxes.";

charbuffer[]=newchar[source.length()];

source.getChars(0,source.length(),buffer,0);

 

FileWriterf0=newFileWriter("file1.txt");

for(inti=0;i<buffer.length;i+=2){

f0.write(buffer[i]);

}

f0.close();

 

FileWriterf1=newFileWriter("file2.txt");

f1.write(buffer);

f1.close();

 

FileWriterf2=newFileWriter("file3.txt");

 

f2.write(buffer,buffer.length-buffer.length/4,buffer.length/4);

f2.close();

}

}

 

CharArrayReader

 

CharArrayReader是一個把字符數組做爲源的輸入流的實現。該類有兩個構造函數,每個都須要一個字符數組提供數據源:

CharArrayReader(chararray[])

CharArrayReader(chararray[],intstart,intnumChars)

這裏,array是輸入源。第二個構造函數從你的字符數組的子集建立了一個Reader,該子集以start指定的索引開始,長度爲numChars。

下面的例子用到了上述CharArrayReader的兩個構造函數:

//DemonstrateCharArrayReader.

importjava.io.*;

 

publicclassCharArrayReaderDemo{

publicstaticvoidmain(Stringargs[])throwsIOException{

Stringtmp="abcdefghijklmnopqrstuvwxyz";

intlength=tmp.length();

charc[]=newchar[length];

 

tmp.getChars(0,length,c,0);

CharArrayReaderinput1=newCharArrayReader(c);

CharArrayReaderinput2=newCharArrayReader(c,0,5);

 

inti;

System.out.println("input1is:");

while((i=input1.read())!=-1){

System.out.print((char)i);

}

System.out.println();

 

System.out.println("input2is:");

while((i=input2.read())!=-1){

System.out.print((char)i);

}

System.out.println();

}

}

input1對象由所有的小寫字母構造,而input2值包含最初的5個字符。下面是輸出:

input1is:

abcdefghijklmnopqrstuvwxyz

input2is:

abcde

CharArrayWriter

 

CharArrayWriter實現了以數組做爲目標的輸出流。CharArrayWriter有兩個構造函數:

CharArrayWriter()

CharArrayWriter(intnumChars)

第一種形式,建立了一個默認長度的緩衝器。第二種形式,緩衝器長度由numChars指定。緩衝器保存在CharArrayWriter的buf成員中。緩衝器大小在須要的狀況下能夠自動增加。緩衝器保持的字符數包含在CharArrayWriter的count成員中。buf和count都是受保護的域。

下面的例子闡述了CharArrayWriter,咱們繼續使用前面顯示的ByteArrayOutputStream例子中演示的程序。它的輸出與之前的例子輸出相同:

//DemonstrateCharArrayWriter.

importjava.io.*;

 

classCharArrayWriterDemo{

publicstaticvoidmain(Stringargs[])throwsIOException{

CharArrayWriterf=newCharArrayWriter();

Strings="Thisshouldendupinthearray";

charbuf[]=newchar[s.length()];

 

s.getChars(0,s.length(),buf,0);

f.write(buf);

System.out.println("Bufferasastring");

System.out.println(f.toString());

System.out.println("Intoarray");

 

charc[]=f.toCharArray();

for(inti=0;i<c.length;i++){

System.out.print(c[i]);

}

 

System.out.println("\nToaFileWriter()");

FileWriterf2=newFileWriter("test.txt");

f.writeTo(f2);

f2.close();

System.out.println("Doingareset");

f.reset();

for(inti=0;i<3;i++)

f.write('X');

System.out.println(f.toString());

}

}

BufferedReader

 

BufferedReader經過緩衝輸入提升性能。它有兩個構造函數:

BufferedReader(ReaderinputStream)

BufferedReader(ReaderinputStream,intbufSize)

第一種形式建立一個默認緩衝器長度的緩衝字符流。第二種形式,緩衝器長度由bufSize傳入。

和字節流的狀況相同,緩衝一個輸入字符流一樣提供支持可用緩衝器中流內反向移動的基礎。爲支持這點,BufferedReader實現了mark()和reset()方法,而且BufferedReader.markSupported()返回true.。

下面的例子改寫了前面的BufferedInputStream例子,它用一個BufferedReader字符流而不是用一個緩衝字節流。和之前同樣,它用mark()和reset()方法解析一個做爲版權記號的HTML實體引用的流。這樣的引用以&符號開始,以分號(;)結束,沒有任何空格。例子輸入有兩個&字符,用來顯示何處reset()發生,何處不發生的狀況。輸出與前面的輸出相同。

//Usebufferedinput.

importjava.io.*;

 

classBufferedReaderDemo{

publicstaticvoidmain(Stringargs[])throwsIOException{

Strings="Thisisa&copy;copyrightsymbol"+

"butthisis&copynot.\n";

charbuf[]=newchar[s.length()];

s.getChars(0,s.length(),buf,0);

CharArrayReaderin=newCharArrayReader(buf);

BufferedReaderf=newBufferedReader(in);

intc;

booleanmarked=false;

while((c=f.read())!=-1){

switch(c){

case'&':

if(!marked){

f.mark(32);

marked=true;

}else{

marked=false;

}

break;

case';':

if(marked){

marked=false;

System.out.print("(c)");

}else

System.out.print((char)c);

break;

case'':

if(marked){

marked=false;

f.reset();

System.out.print("&");

}else

System.out.print((char)c);

break;

default:

if(!marked)

System.out.print((char)c);

break;

}

}

}

}

 

BufferedWriter

 

BufferedWriter是一個增長了flush()方法的Writer。flush()方法能夠用來確保數據緩衝器確實被寫到實際的輸出流。用BufferedWriter能夠經過減少數據被實際的寫到輸出流的次數而提升程序的性能。

BufferedWriter有兩個構造函數:

BufferedWriter(WriteroutputStream)

BufferedWriter(WriteroutputStream,intbufSize)

第一種形式建立了使用默認大小緩衝器的緩衝流。第二種形式中,緩衝器大小是由bufSize參數傳入的。

PushbackReader

 

PushbackReader類容許一個或多個字符被送回輸入流。這使你能夠對輸入流進行預測。

下面是它的兩個構造函數:

PushbackReader(ReaderinputStream)

PushbackReader(ReaderinputStream,intbufSize)

第一種形式建立了一個容許單個字節被推回的緩衝流。第二種形式,推回緩衝器的大小由bufSize參數傳入。

PushbackReader提供了unread()方法。該方法返回一個或多個字符到調用的輸入流。

它有下面的三種形式:

voidunread(intch)

voidunread(charbuffer[])

voidunread(charbuffer[],intoffset,intnumChars)

第一種形式推回ch傳入的字符。它是被併發調用的read()返回的下一個字符。第二種形式返回buffer中的字符。第三種形式推回buffer中從offset開始的numChars個字符。若是在推回緩衝器爲滿的條件下試圖返回一個字符,一個IOException異常將被引起。

下面的例子重寫了前面的PushBackInputStream例子,用PushbackReader代替了PushBackInputStream。和之前同樣,它演示了一個編程語言解析器怎樣用一個推回流處理用於比較的==操做符和用於賦值的=操做符之間的不一樣。

//Demonstrateunread().

importjava.io.*;

classPushbackReaderDemo{

publicstaticvoidmain(Stringargs[])throwsIOException{

Strings="if(a==4)a=0;\n";

charbuf[]=newchar[s.length()];

s.getChars(0,s.length(),buf,0);

CharArrayReaderin=newCharArrayReader(buf);

PushbackReaderf=newPushbackReader(in);

intc;

 

while((c=f.read())!=-1){

switch(c){

case'=':

if((c=f.read())=='=')

System.out.print(".eq.");

else{

System.out.print("<-");

f.unread(c);

}

break;

default:

System.out.print((char)c);

break;

}

}

}

}

 

PrintWriter

 

PrintWriter本質上是PrintStream的字符形式的版本。它提供格式化的輸出方法print()和println()。PrintWriter有四個構造函數:

PrintWriter(OutputStreamoutputStream)

PrintWriter(OutputStreamoutputStream,booleanflushOnNewline)

PrintWriter(WriteroutputStream)

PrintWriter(WriteroutputStream,booleanflushOnNewline)

flushOnNewline控制Java是否在每次輸出換行符(\n)時刷新輸出流。若是flushOnNewline爲true,刷新自動發生。若爲false,不進行自動刷新。第一個和第三個構造函數不能自動刷新。

Java的PrintWriter對象支持包括用於Object在內的各類類型的print()和println()方法。若是語句不是一個簡單類型,PrintWriter的方法將調用對象的toString()方法,而後輸出結果。

使用流式輸入/輸出

 

 

下面的例子演示了幾個Java的輸入/輸出字符流類和方法。該程序執行標準wc(字數統計)命令。程序有兩個模式:若是沒有語句提供的文件名存在,程序對標準輸入流進行操做。若是一個或多個文件名被指定,程序對每個文件進行操做。

//Awordcountingutility.

importjava.io.*;

 

classWordCount{

publicstaticintwords=0;

publicstaticintlines=0;

publicstaticintchars=0;

 

publicstaticvoidwc(InputStreamReaderisr)

throwsIOException{

intc=0;

booleanlastWhite=true;

StringwhiteSpace="\t\n\r";

 

while((c=isr.read())!=-1){

//Countcharacters

chars++;

//Countlines

if(c=='\n'){

lines++;

}

//Countwordsbydetectingthestartofaword

intindex=whiteSpace.indexOf(c);

if(index==-1){

if(lastWhite==true){

++words;

}

lastWhite=false;

}

else{

lastWhite=true;

}

}

if(chars!=0){

++lines;

}

}

 

publicstaticvoidmain(Stringargs[]){

FileReaderfr;

try{

if(args.length==0){//We'reworkingwithstdin

wc(newInputStreamReader(System.in));

}

else{//We'reworkingwithalistoffiles

for(inti=0;i<args.length;i++){

fr=newFileReader(args[i]);

wc(fr);

}

}

}

catch(IOExceptione){

return;

}

System.out.println(lines+""+words+""+chars);

}

}

wc()方法對任何輸入流進行操做而且計算字符數,行數和字數。它在lastNotWhite裏追蹤字數的奇偶和空格。當在沒有參數的狀況下執行時,WordCount以System.in爲源流生成一個InputStreamReader對象。該流而後被傳遞到實際計數的wc()方法。當在有一個或多個參數的狀況下執行時,WordCount假設這些文件名存在並給每個文件建立FileReader,傳遞保存結果的FileReader對象給wc()方法。兩種狀況下,在退出以前都打印結果。

用StreamTokenizer(流標記)來改善wc()

 

在輸入流中一個更好的尋找模式的方法使用Java的另外一個輸入/輸出類:StreamTokenizer。與第16章的StringTokenizer類似,StreamTokenizer把InputStream拆解到被字符組界定的標記(token)中。它有下面的構造函數:

StreamTokenizer(ReaderinStream)

這裏,inStream必須具備Reader的某種形式。

StreamTokenizer定義了幾個方法。該例中,咱們僅用到少數幾個。爲重置分隔符的默認設置,咱們使用resetSyntax()方法。分隔符的默認設置與標記表徵的Java程序完美和諧,並且是爲該例專用的。咱們規定咱們的標記,即「字」,是兩邊都有空格的明顯字符組成的連續的字符串。

咱們用eolIsSignificant()來保證換行符做爲標記被傳遞,因此咱們能夠和計算字數同樣計算行數。它的一般形式以下:

voideolIsSignificant(booleaneolFlag)

若是eolFlag爲true,行結束符做爲標記返回;若爲false,行結束符被忽略。

wordChars()方法用來指定能夠用於字的字符範圍。它的一般形式以下:

voidwordChars(intstart,intend)

這裏,start和end指定了有效字符的範圍。程序中,從33到255範圍內的字符都是有效字符。

空格符由whitespaceChars()說明。它的通常形式以下:

voidwhitespaceChars(intstart,intend)

這裏,start和end指定了有效空格符的範圍。

下一個標記經過調用nextToken()從輸入流得到,它返回標記的類型。

StreamTokenizer定義個四個int型常量:TT_EOF,TT_EOL,TT_NUMBER和TT_WORD。

有三個實例變量。nval是一個公開的double型變量,用來保存可識別的字數的值。sval是一個publicString型變量,用來保存可識別的的字的值。ttype是一個int型變量,說明剛剛被nextToken()方法讀取的標記的類型。若是標記是一個字,ttype等於TT_WORD。若是標記爲一個數,ttype等於TT_NUMBER。若是標記是單一字符,ttype包含該字符的值。若是遇到一個行結束狀況,ttype等於TT_EOL(這假定了參數爲true調用eolIsSignificant())。若是遇到流的結尾,ttype等於TT_EOF。

用StreamTokenizer修改過的字數計算程序顯示以下:

//EnhancedwordcountprogramthatusesaStreamTokenizer

importjava.io.*;

classWordCount{

publicstaticintwords=0;

publicstaticintlines=0;

publicstaticintchars=0;

 

publicstaticvoidwc(Readerr)throwsIOException{

StreamTokenizertok=newStreamTokenizer(r);

 

tok.resetSyntax();

tok.wordChars(33,255);

tok.whitespaceChars(0,'');

tok.eolIsSignificant(true);

 

while(tok.nextToken()!=tok.TT_EOF){

switch(tok.ttype){

casetok.TT_EOL:

lines++;

chars++;

break;

casetok.TT_WORD:

words++;

default://FALLSTHROUGH

chars+=tok.sval.length();

break;

}

}

}

 

publicstaticvoidmain(Stringargs[]){

if(args.length==0){//We'reworkingwithstdin

try{

wc(newInputStreamReader(System.in));

System.out.println(lines+""+words+""+chars);

}catch(IOExceptione){};

}else{//We'reworkingwithalistoffiles

inttwords=0,tchars=0,tlines=0;

for(inti=0;i<args.length;i++){

try{

words=chars=lines=0;

wc(newFileReader(args[i]));

twords+=words;

tchars+=chars;

tlines+=lines;

System.out.println(args[i]+":"+

lines+""+words+""+chars);

}catch(IOExceptione){

System.out.println(args[i]+":error.");

}

}

System.out.println("total:"+

tlines+""+twords+""+tchars);

}

}

}

序列化

 

 

序列化(serialization)是把一個對象的狀態寫入一個字節流的過程。當你想要把你的程序狀態存到一個固定的存儲區域例如文件時,它是很管用的。稍後一點時間,你就能夠運用序列化過程存儲這些對象。

序列化也要執行遠程方法調用(RMI)。RMI容許一臺機器上的Java對象調用不一樣機器上的Java對象方法。對象能夠做爲一個參數提供給那個遠程方法。發送機序列化該對象並傳送它。接受機反序列化它(關於RMI的更多內容請參看第24章)。

假設一個被序列化的對象引用了其餘對象,一樣,其餘對象又引用了更多的對象。這一系列的對象和它們的關係造成了一個順序圖表。在這個對象圖表中也有循環引用。也就是說,對象X能夠含有一個對象Y的引用,對象Y一樣能夠包含一個對象X的引用。對象一樣能夠包含它們本身的引用。對象序列化和反序列化的工具被設計出來並在這一假定條件下運行良好。若是你試圖序列化一個對象圖表中頂層的對象,全部的其餘的引用對象都被循環的定位和序列化。一樣,在反序列化過程當中,全部的這些對象以及它們的引用都被正確的恢復。

下面是支持序列化的接口和類的概述。

Serializable接口

 

只有一個實現Serializable接口的對象能夠被序列化工具存儲和恢復。Serializable接口沒有定義任何成員。它只用來表示一個類能夠被序列化。若是一個類能夠序列化,它的全部子類均可以序列化。

聲明成transient的變量不被序列化工具存儲。一樣,static變量也不被存儲。

Externalizable接口

 

Java的序列化和反序列化的工具被設計出來,因此不少存儲和恢復對象狀態的工做自動進行。然而,在某些狀況下,程序員必須控制這些過程。例如,在須要使用壓縮或加密技術時,Externalizable接口爲這些狀況而設計。

Externalizable接口定義了兩個方法:

voidreadExternal(ObjectInputinStream)

throwsIOException,ClassNotFoundException

voidwriteExternal(ObjectOutputoutStream)

throwsIOException

這些方法中,inStream是對象被讀取的字節流,outStream是對象被寫入的字節流。

ObjectOutput接口

 

ObjectOutput繼承DataOutput接口而且支持對象序列化。它定義的方法顯示於表10-5中。特別注意writeObject()方法,它被稱爲序列化一個對象。全部這些方法在出錯狀況下引起IOException異常。

 

 

 

 

 

ObjectOutputStream類

 

ObjectOutputStream類繼承OutputStream類和實現ObjectOutput接口。它負責向流寫入對象。該類的構造函數以下:

ObjectOutputStream(OutputStreamoutStream)throwsIOException

參數outStream是序列化的對象將要寫入的輸出流。

該類中最經常使用的方法列於表10-6。它們在出錯狀況下引起IOException異常。Java2給ObjectOuputStream增長了一個名爲PutField的內部類。該類有助於持久域的編寫,它的用法超出了本書的範圍。

 

 

ObjectInput接口繼承DataInput接口而且定義了表10-7中的方法。它支持對象序列化。

特別注意readObject()方法,它叫反序列化對象。全部這些方法在出錯狀況下引起IOException異常。

 

 

ObjectInputStream繼承InputStream類並實現ObjectInput接口。ObjectInputStream負責從流中讀取對象。該類的構造函數以下:

ObjectInputStream(InputStreaminStream)

throwsIOException,StreamCorruptedException

參數inStream是序列化對象將被讀取的輸入流。

該類中最經常使用的方法列於表10-8。它們在出錯狀況下將引起IOException異常。Java2爲ObjectInputStream增長了一個名爲GetField的內部類。它幫助讀取持久域,它的用途超出了本書的範圍。而且,Java2中不被同意的readLine()方法不該該繼續使用。

 

 

 

下面的程序說明了怎樣實現對象序列化和反序列化。它由實例化一個MyClass類的對象開始。該對象有三個實例變量,它們的類型分別是String,int和double。這是咱們但願存儲和恢復的信息。

FileOutputStream被建立,引用了一個名爲「serial」的文件。爲該文件流建立一個ObjectOutputStream。ObjectOutputStream的writeObject()方法用來序列化對象。對象的輸出流被刷新和關閉。

而後,引用名爲「serial」的文件建立一個FileInputStream類併爲該文件建立一個ObjectInputStream類。ObjectInputStream的readObject()方法用來反序列化對象。而後對象輸入流被關閉。

注意MyClass被定義成實現Serializable接口。若是不這樣作,將會引起一個NotSerializableException異常。試圖作一些把MyClass實例變量聲明成transient的實驗。那些數據在序列化過程當中不被保存。

importjava.io.*;

 

publicclassSerializationDemo{

publicstaticvoidmain(Stringargs[]){

 

//Objectserialization

try{

MyClassobject1=newMyClass("Hello",-7,2.7e10);

System.out.println("object1:"+object1);

FileOutputStreamfos=newFileOutputStream("serial");

ObjectOutputStreamoos=newObjectOutputStream(fos);

oos.writeObject(object1);

oos.flush();

oos.close();

}

catch(Exceptione){

System.out.println("Exceptionduringserialization:"+e);

System.exit(0);

}

 

//Objectdeserialization

try{

MyClassobject2;

FileInputStreamfis=newFileInputStream("serial");

ObjectInputStreamois=newObjectInputStream(fis);

object2=(MyClass)ois.readObject();

ois.close();

System.out.println("object2:"+object2);

}

catch(Exceptione){

System.out.println("Exceptionduringdeserialization:"+e);

System.exit(0);

}

}

}

 

classMyClassimplementsSerializable{

Strings;

inti;

doubled;

publicMyClass(Strings,inti,doubled){

this.s=s;

this.i=i;

this.d=d;

}

publicStringtoString(){

return"s="+s+";i="+i+";d="+d;

}

}

該程序說明了object1和object2的實例變量是同樣的。輸出以下:

object1:s=Hello;i=-7;d=2.7E10

object2:s=Hello;i=-7;d=2.7E10

 

流的益處

 

 

Java的輸入輸出的流式接口爲複雜而繁重的任務提供了一個簡潔的抽象。過濾流類的組合容許你動態創建客戶端流式接口來配合數據傳輸要求。繼承高級流類InputStream、InputStreamReader、Reader和Writer類的Java程序在未來(即便建立了新的和改進的具體類)也能獲得合理運用。就像你將在下一章看到的,這種模式在咱們從基於文件系統的流轉換到網絡和套接字流時工做良好。最後,對象序列化有望在將來的Java編程中扮演一個愈來愈重要的角色。Java的序列化輸入/輸出類爲這些有時顯得十分複雜的任務提供了便攜的解決方法。



 

實踐問題:

 

1.      若是要把水庫中的水輸送到千家萬戶,須要構造各類管道;這些管道有粗有細、有又長又短,且型號各不一樣;若是是輸送暖氣,那麼中途還須要有加壓站等設備;這些管道設備和本章學習的IO流的異同之處在哪裏?

 

2,一樣是一件事情,若是對於不一樣人處理的方法不一樣;一樣是一件事物,人和人的見解不相同;古代詩人有詩曰:「橫當作嶺側成峯,遠近高低各不一樣」;這些和咱們本章所學的知識能不能聯繫起來呢?

 



 

小結:

 

 

在本章中,咱們主要學習了:

 

u      高級IO的處理流;

u      不一樣的IO的不一樣處理方法;

 



 

英語詞彙:

 

 

英文             全文                                         中文

 

Java       Java              一種高效流行的編程語言

JDK    JAVADevelopkit       Java開發工具包

 



 

練習項目:

 

 

用兩種方式讀一個文件並使用第一個文件的內容寫出另外一個文件;1.使用純的字符流處理讀寫。2.使用純字節處理讀寫。並總結兩種的利弊。寫出本身的感覺;(提示:以上所說「處理」都指中間的流的過程處理)

相關文章
相關標籤/搜索