java之十 高级IO流

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对象:并发

File(StringdirectoryPath)app

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.使用纯字节处理读写。并总结两种的利弊。写出本身的感觉;(提示:以上所说“处理”都指中间的流的过程处理)
相关文章
相关标签/搜索