《list17》由会员上传分享,免费在线阅读,更多相关内容在学术论文-天天文库。
----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方第17章输入/输出:探究java.io本章探究为输入/输出操作提供支持的java.io包。在第12章,我们介绍了Java的输入/输出系统。现在,我们要更深入的研究Java输入/输出系统。所有的程序员都知道,多数程序在不获取外部数据的情况下不能顺利完成目标。数据从一个输入源获得。程序的结果被送到输出目的地。Java中,这些源和目的地被广泛的定义。例如一个网络连接器,内存缓冲区或磁盘文件可以被Java输入/输出类熟练的操作。尽管从物理上很难说明,这些外设都由相同的抽象体流(stream)来处理。流,在第12章解释过,是一个生产或消费信息的逻辑实体。流通过Java输入/输出系统与物理设备相连。尽管与之相连的实际的物理设备各不相同,所有的流都以同样的方式运转。注意:Java流式输入/输出的概述,参看第12章。17.1Java输入/输出类和接口java.io定义的输入/输出类列于下表:BufferedInputStreamFileWriterPipedInputStreamBufferedOutputStreamFilterInputStreamPipedOutputStreamBufferedReaderFilterOutputStreamPipedReaderBufferedWriterFilterReaderPipedWriterByteArrayInputStreamFilterWriterPrintStreamByteArrayOutputStreamInputStreamPrintWriterCharArrayReaderInputStreamReaderPushbackInputStreamCharArrayWriterLineNumberReaderPushbackReaderDataInputStreamObjectInputStreamRandomAccessFileDataOutputStreamObjectInputStream.GetFieldReaderFileObjectOutputStreamSequenceInputStreamFileDescriptorObjectOutputStream.PutFieldSerializablePermissionFileInputStreamObjectStreamClassStreamTokenizerFileOutputStreamObjectStreamFieldStringReaderFilePermissionOutputStreamStringWriterFileReaderOutputStreamWriterWriterObjectInputStream.GetField和ObjectOutputStream.PutField是Java2新添的内部类。java.io包还包含两个不受java2欢迎的类,这两个类没有在上表中列出:LineNumber----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方
1----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方InputStream和StringBufferInputStream。新代码不应该使用两个类。下面是由java.io定义的接口:DataInputFilenameFilterObjectOutputDataOutputObjectInputObjectStreamConstantsExternalizableObjectInputValidationSerializableFileFilterFileFilter接口是Java2新增的。java.io包中有很多类和接口。包括字节和字符流,对象序列化(对象的存储和释放)。本章讲述几个最常用的I/O成员,从最独特的File开始。17.2File(文件类)尽管java.io定义的大多数类是实行流式操作的,File类不是。它直接处理文件和文件系统。也就是说,File类没有指定信息怎样从文件读取或向文件存储;它描述了文件本身的属性。File对象用来获取或处理与磁盘文件相关的信息,例如权限,时间,日期和目录路径。此外,File还浏览子目录层次结构。很多程序中文件是数据的根源和目标。尽管它们在小应用程序中因为安全原因而受到严格限制,文件仍是存储固定和共享信息的主要资源。Java中的目录当成File对待,它具有附加的属性——一个可以被list( )方法检测的文件名列表。下面的构造函数可以用来生成File对象: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使用反斜线(\----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方
2----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方)的约定,你需要在字符串内使用它的转义序列(\\)。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:COPYRIGHTPath:/java/COPYRIGHTAbsPath:/java/COPYRIGHTParent:/javaexistsiswriteableisreadableisnotadirectoryisnormalfileisabsoluteFilelastmodified:812465204000Filesize:695Bytes大多数File方法是自说明的,但isFile( )和isAbsolute( )不是。isFile( )----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方
3----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方在被文件调用时返回true,在被目录调用时返回false。并且,isFile( )被一些专用文件调用时返回false,例如设备驱动程序和命名管道,所以该方法可用来判定文件是否作为文件执行。isAbsolute( )方法在文件拥有绝对路径时返回true,若是相对路径则返回false。File还包括两个有用的实用工具方法。第一个是renameTo( ),显示如下:booleanrenameTo(FilenewName)这里,由newName指定的文件名变成了所调用的File对象的新的名称。如果更名成功则返回ture,文件不能被重命名(例如,你试图重命名文件以使它从一个目录转到另一个目录,或者你使用了一个已经存在的文件名),则返回false。第二个实用工具方法是delete( ),该方法删除由被调用的File对象的路径指定的磁盘文件。它的形式如下:booleandelete( )同样可以在目录为空时用delete( )删除目录。如果删除了文件,delete( )返回true,如果文件不能被删除则返回false。Java2为File类增添了一些新的方法,你会发现在某些场合这些新增方法很有用。一些最有趣的方法显示如下:方法描述voiddeleteOnExit( )在java虚拟机终止时删除与调用对象相关的文件booleanisHidden( )如果调用的文件是隐藏的,返回true;否则返回false。booleansetLastModified(longmillisec)设置由millisec指定的调用文件的时间标志,Millisec是从1970年1月1号开始的标准时间(UTC)的毫秒数booleansetReadOnly( )设置调用文件为只读并且,因为File类现在支持Comparable接口,compareTo( )方法也被支持。17.2.1目录目录是一个包含其他文件和路径列表的File类。当你创建一个File对象且它是目录时,isDirectory( )方法返回ture。这种情况下,可以调用该对象的list( )方法来提取该目录内部其他文件和目录的列表。该方法有两种形式。第一种形式如下:String[]list( )文件列表在一个String对象数组中返回。下面显示的程序说明怎样用list( )来检查一个目录的内容://Usingdirectories.importjava.io.File;classDirList{publicstaticvoidmain(Stringargs[]){Stringdirname="/java";Filef1=new----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方
4----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方File(dirname);if(f1.isDirectory()){System.out.println("Directoryof"+dirname);Strings[]=f1.list();for(inti=0;i 5----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方下面显示的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 6----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方如果文件被包括在列表中(即与path参数匹配的文件),accept( )方法返回true,如果不被包括,则返回false。17.2.4创建目录另外两个有用的File类的方法是mkdir( )和mkdirs( )。mkdir( )方法创建了一个目录,创建成功返回true,创建失败返回false。创建失败是指File对象指定的目录已经存在,或者是因为整个路径不存在而不能创建目录。创建路径不存在的目录,用mkdirs( )的方法。它创建目录以及该目录所有的父目录。17.3流类Java的流式输入/输出建立在四个抽象类的基础上:InputStream,OutputStream,Reader和Writer。这些类在第12章中有过简要的讨论。它们用来创建具体流式子类。尽管程序通过具体子类执行输入/输出操作,顶层的类定义了所有流类的基础通用功能。InputStream和OutputStream设计成字节流类。Reader和Writer为字符流设计。字节流类和字符流类形成分离的层次结构。一般说来,处理字符或字符串时应使用字符流类,处理字节或二进制对象时应用字节流类。下面分别讲述字节流和字符流类。17.4字节流字节流类为处理字节式输入/输出提供了丰富的环境。一个字节流可以和其他任何类型的对象并用,包括二进制数据。这样的多功能性使得字节流对很多类型的程序都很重要。因为字节流类以InputStream和OutputStream为顶层,我们就从讨论这两个类开始。17.4.1InputStream(输入流)InputStream是一个定义了Java流式字节输入模式的抽象类。该类的所有方法在出错条件下引发一个IOException异常。表17-1显示了InputStream的方法。表17-1InputStream定义的方法方法描述intavailable()返回当前可读的输入字节数voidclose()关闭输入源。关闭之后的读取会产生IOException异常voidmark(intnumBytes)在输入流的当前点放置一个标记。该流在读取numBytes个字节前都保持有效----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 7----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方booleanmarkSupported()如果调用的流支持mark()/reset()就返回trueintread()如果下一个字节可读则返回一个整型,遇到文件尾时返回-1intread(bytebuffer[])试图读取buffer.length个字节到buffer中,并返回实际成功读取的字节数。遇到文件尾时返回-1intread(bytebuffer[],intoffset,intnumBytes)试图读取buffer中从buffer[offset]开始的numBytes个字节,返回实际读取的字节数。遇到文件尾时返回-1voidreset()重新设置输入指针到先前设置的标志处longskip(longnumBytes)忽略numBytes个输入字节,返回实际忽略的字节数17.4.2OutputStream(输出流)OutputStream是定义了流式字节输出模式的抽象类。该类的所有方法返回一个void值并且在出错情况下引发一个IOException异常。表17-2显示了OutputStream的方法。表17-2OutputStream定义的方法方法描述voidclose()关闭输出流。关闭后的写操作会产生IOException异常voidflush()定制输出状态以使每个缓冲器都被清除,也就是刷新输出缓冲区voidwrite(intb)向输出流写入单个字节。注意参数是一个整型数,它允许你不必把参数转换成字节型就可以调用write()voidwrite(bytebuffer[])向一个输出流写一个完整的字节数组voidwrite(bytebuffer[],intoffset,intnumBytes)写数组buffer以buffer[offset]为起点的numBytes个字节区域内的内容注意:多数在表17-1和表17-2中描述的方法由InputStream和OutputStream的子类实现,但mark( )和reset( )方法除外。注意下面讨论的每个子类中它们的使用和不用情况。17.4.3FileInputStream(文件输入流)FileInputStream类创建一个能从文件读取字节的InputStream类,它的两个常用的构造函数如下:FileInputStream(Stringfilepath)FileInputStream(FilefileObj)----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 8----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方它们都能引发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 9StillAvailable:"+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(" 10StillAvailable:"+(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){----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 11----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方System.err.println("couldn'tread"+n/2+"bytes.");}System.out.println(newString(b,0,b.length));System.out.println(" 12StillAvailable:"+f.available());f.close();}}下面是该程序的输出:TotalAvailableBytes:1433First35bytesofthefileoneread()atatime//DemonstrateFileInputStream.imStillAvailable:1398Readingthenext35withoneread(b[])portjava.io.*;classFileInputSStillAvailable:1363Skippinghalfofremainingbyteswithskip()StillAvailable:682Reading17intotheendofarrayportjava.io.*;read(b)!=n){SStillAvailable:665这个有些刻意创作的例子说明了怎样读取数据的三种方法,怎样跳过输入以及怎样检查流中可以获得数据的数目。17.4.4FileOutputStream(文件输出流)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异常。----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 13----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方//DemonstrateFileOutputStream.importjava.io.*;classFileOutputStreamDemo{publicstaticvoidmain(Stringargs[])throwsException{Stringsource="Nowisthetimeforallgoodmen 14"+"tocometotheaidoftheircountry 15"+"andpaytheirduetaxes.";bytebuf[]=source.getBytes();OutputStreamf0=newFileOutputStream("file1.txt");for(inti=0;i 16----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方下面的例子创建了两个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();}}}该例先从流中读取每个字符,然后以小写字母形式打印。然后重新设置流并从头读起,这次在打印之前先将字母转换成大写字母。下面是输出:abcABC17.4.6ByteArrayOutputStream(字节数组输出流)ByteArrayOutputStream是一个把字节数组当作输出流的实现。ByteArrayOutputStream----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 17----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方有两个构造函数,如下: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 18ToanOutputStream()");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怎样结束。BufferasastringThisshouldendupinthearrayIntoarrayThisshouldendupinthearrayToanOutputStream()DoingaresetXXX----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 19----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方该例用writeTo( )这一便捷的方法将f的内容写入test.txt,检查在前面例子中生成的test.txt文件内容,结果如下:Thisshouldendupinthearray17.4.7过滤字节流过滤流(filteredstream)仅仅是底层透明地提供扩展功能的输入流(输出流)的包装。这些流一般由普通类的方法(即过滤流的一个超类)访问。典型的扩展是缓冲,字符转换和原始数据转换。这些过滤字节流是FilterInputStream和FilterOutputStream。它们的构造函数如下:FilterOutputStream(OutputStreamos)FilterInputStream(InputStreamis)这些类提供的方法和InputStream及OutputStream类的方法相同。17.4.8缓冲字节流对于字节流,缓冲流(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实体的引用为版权信息的情况。这个引用以一个&符号开始以分号(;)结束,没有任何空格。例子输入由两个&----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 20----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方符号来说明何处reset( )发生,何处不发生的情况。//Usebufferedinput.importjava.io.*;classBufferedInputStreamDemo{publicstaticvoidmain(Stringargs[])throwsIOException{Strings="Thisisa©copyrightsymbol"+"butthisis©not. 21";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)");}elseSystem.out.print((char)c);break;case'':if(marked){marked=false;f.reset();System.out.print("&");}elseSystem.out.print((char)c);break;default:if(!marked)System.out.print((char)c);break;}}}}注意该例运用mark(32),该方法保存接下来所读取的32个字节(这个数量对所有的实体引用都足够)。下面是程序的输出:----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 22----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方Thisisa(c)copyrightsymbolbutthisis©not.警告:在缓冲器中使用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( )来处理==操作符和=操作符之间的不同的。----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 23----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方//Demonstrateunread().importjava.io.*;classPushbackInputStreamDemo{publicstaticvoidmain(Stringargs[])throwsIOException{Strings="if(a==4)a=0; 24";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( )的任何流类。17.4.9SequenceInputStream(顺序输入流)SequenceInputStream类允许连接多个InputStream流。SequenceInputStream的构造不同于任何其他的InputStream。SequenceInputStream构造函数要么使用一对InputStream,要么用InputStream的一个Enumeration,显示如下:SequenceInputStream(InputStreamfirst,InputStreamsecond)SequenceInputStream(EnumerationstreamEnum)操作上来说,该类满足读取完第一个InputStream后转去读取第二个流的读取要求。使用Enumeration的情况下,它将继续读取所有InputStream流直到最后一个被读完。----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 25----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方下面是用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依次打开每个文件,该程序打印了两个文件的内容。17.4.10PrintStream(打印流)PrintStream具有本书开始以来我们在System文件句柄使用过的System.out所有的格式化性能。PrintStream有两个构造函数:PrintStream(OutputStreamoutputStream)PrintStream(OutputStreamoutputStream,booleanflushOnNewline)----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 26----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方当flushOnNewline控制Java每次刷新输出流时,输出一个换行符( 27)。如果flushOnNewline为true,自动刷新。若为false,刷新不能自动进行。第一个构造函数不支持自动刷新。Java的PrintStream对象支持包括Object在内的各种类型的print( )和println( )方法。如果参数不是一个简单类型,PrintStream方法将调用对象的toString( )方法,然后打印结果。17.4.11RandomAccessFile(随机访问文件类)RandomAccessFile包装了一个随机访问的文件。它不是派生于InputStream和OutputStream,而是实现定义了基本输入/输出方法的DataInput和DataOutput接口。它同样支持定位请求——也就是说,可以在文件内部放置文件指针。它有两个构造函数:RandomAccessFile(FilefileObj,Stringaccess) throwsFileNotFoundExceptionRandomAccessFile(Stringfilename,Stringaccess) throwsFileNotFoundException第一种形式,fileObj指定了作为File对象打开的文件的名称。第二种形式,文件名是由filename参数传入的。两种情况下,access都决定允许访问何种文件类型。如果是“r”,那么文件可读不可写,如果是“rw”,文件以读写模式打开。下面所示的seek( )方法,用来设置文件内部文件指针的当前位置:voidseek(longnewPos)throwsIOException这里,newPos指文件指针从文件开始以字节方式指定新位置。调用seek( )方法后,接下来的读或写操作将在文件的新位置发生。RandomAccessFile实现了用来读写随机访问文件的标准的输入和输出方法。下面是Java2增添的新方法setLength( )。它有下面的形式:voidsetLength(longlen)throwsIOException该方法通过指定的len设置正在调用的文件的长度。该方法可以增长或缩短一个文件。如果文件被加长,增加的部分是未定义的。17.5字符流尽管字节流提供了处理任何类型输入/输出操作的足够的功能,它们不能直接操作Unicode字符。既然Java的一个主要目的是支持“只写一次,到处运行”的哲学,包括直接的字符输入/输出支持是必要的。本节将讨论几个字符输入/输出类。如前所述,字符流层次结构的顶层是Reader和Writer抽象类。我们将从它们开始。----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 28----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方注意:如第12章讨论过的,字符输入/输出类是在java的1.1版本中新加的。由此,你仍然可以发现遗留下的程序代码在应该使用字符流时却使用了字节流。当遇到这种代码,最好更新它。17.5.1ReaderReader是定义Java的流式字符输入模式的抽象类。该类的所有方法在出错情况下都将引发IOException异常。表17-3给出了Reader类中的方法。表17-3Reader定义的方法方法描述abstractvoidclose()关闭输入源。进一步的读取将会产生IOException异常voidmark(intnumChars)在输入流的当前位置设立一个标志。该输入流在numChars个字符被读取之前有效booleanmarkSupported()该流支持mark()/reset()则返回trueintread()如果调用的输入流的下一个字符可读则返回一个整型。遇到文件尾时返回-1intread(charbuffer[])试图读取buffer中的buffer.length个字符,返回实际成功读取的字符数。遇到文件尾返回-1abstractintread(charbuffer[],intoffset,intnumChars)试图读取buffer中从buffer[offset]开始的numChars个字符,返回实际成功读取的字符数。遇到文件尾返回-1booleanready()如果下一个输入请求不等待则返回true,否则返回falsevoidreset()设置输入指针到先前设立的标志处longskip(longnumChars)跳过numChars个输入字符,返回跳过的字符数17.5.2WriterWriter是定义流式字符输出的抽象类。所有该类的方法都返回一个void值并在出错条件下引发IOException异常。表17-4给出了Writer类中方法。表17-4Writer定义的方法方法描述abstractvoidclose()关闭输出流。关闭后的写操作会产生IOException异常abstractvoidflush()定制输出状态以使每个缓冲器都被清除。也就是刷新输出缓冲voidwrite(intch)向输出流写入单个字符。注意参数是一个整型,它允许你不必把参数转换成字符型就可以调用write()voidwrite(charbuffer[])向一个输出流写一个完整的字符数组----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 29----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方续表方法描述abstractvoidwrite(charbuffer[],intoffset,intnumChars)向调用的输出流写入数组buffer以buffer[offset]为起点的numChars个字符区域内的内容voidwrite(Stringstr)向调用的输出流写strvoidwrite(Stringstr,intoffset,intnumChars)写数组str中以制定的offset为起点的长度为numChars个字符区域内的内容17.5.3FileReaderFileReader类创建了一个可以读取文件内容的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();}}17.5.4FileWriterFileWriter创建一个可以写文件的Writer类。它最常用的构造函数如下:FileWriter(StringfilePath)FileWriter(StringfilePath,booleanappend)FileWriter(FilefileObj)它们可以引发IOException或SecurityException异常。这里,filePath是文件的完全路径,----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 30----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方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 31"+"tocometotheaidoftheircountry 32"+"andpaytheirduetaxes.";charbuffer[]=newchar[source.length()];source.getChars(0,source.length(),buffer,0);FileWriterf0=newFileWriter("file1.txt");for(inti=0;i 33----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方java.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:abcdefghijklmnopqrstuvwxyzinput2is:abcde17.5.6CharArrayWriterCharArrayWriter实现了以数组作为目标的输出流。CharArrayWriter有两个构造函数:CharArrayWriter( )CharArrayWriter(intnumChars)第一种形式,创建了一个默认长度的缓冲器。第二种形式,缓冲器长度由numChars指定。缓冲器保存在CharArrayWriter的buf成员中。缓冲器大小在需要的情况下可以自动增长。缓冲器保持的字符数包含在CharArrayWriter的count成员中。buf和count都是受保护的域。下面的例子阐述了CharArrayWriter,我们继续使用前面显示的ByteArrayOutputStream例子中演示的程序。它的输出与以前的例子输出相同://DemonstrateCharArrayWriter.importjava.io.*;classCharArrayWriterDemo{----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 34----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方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 35ToaFileWriter()");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());}}17.5.7BufferedReaderBufferedReader通过缓冲输入提高性能。它有两个构造函数: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©copyrightsymbol"+"butthisis©not. 36";----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 37----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方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)");}elseSystem.out.print((char)c);break;case'':if(marked){marked=false;f.reset();System.out.print("&");}elseSystem.out.print((char)c);break;default:if(!marked)System.out.print((char)c);break;}}}}17.5.8BufferedWriterBufferedWriter是一个增加了flush()方法的Writer。flush()方法可以用来确保数据缓冲器确实被写到实际的输出流。用BufferedWriter可以通过减小数据被实际的写到输出流的次数而提高程序的性能。BufferedWriter有两个构造函数:BufferedWriter(WriteroutputStream)BufferedWriter(WriteroutputStream,intbufSize)----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 38----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方第一种形式创建了使用默认大小缓冲器的缓冲流。第二种形式中,缓冲器大小是由bufSize参数传入的。17.5.9PushbackReaderPushbackReader类允许一个或多个字符被送回输入流。这使你可以对输入流进行预测。下面是它的两个构造函数: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; 39";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);----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 40----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方break;}}}}17.5.10PrintWriterPrintWriter本质上是PrintStream的字符形式的版本。它提供格式化的输出方法print( )和println( )。PrintWriter有四个构造函数:PrintWriter(OutputStreamoutputStream)PrintWriter(OutputStreamoutputStream,booleanflushOnNewline)PrintWriter(WriteroutputStream)PrintWriter(WriteroutputStream,booleanflushOnNewline)flushOnNewline控制Java是否在每次输出换行符( 41)时刷新输出流。如果flushOnNewline为true,刷新自动发生。若为false,不进行自动刷新。第一个和第三个构造函数不能自动刷新。Java的PrintWriter对象支持包括用于Object在内的各种类型的print( )和println( )方法。如果语句不是一个简单类型,PrintWriter的方法将调用对象的toString()方法,然后输出结果。17.6使用流式输入/输出下面的例子演示了几个Java的输入/输出字符流类和方法。该程序执行标准wc(字数统计)命令。程序有两个模式:如果没有语句提供的文件名存在,程序对标准输入流进行操作。如果一个或多个文件名被指定,程序对每一个文件进行操作。//Awordcountingutility.importjava.io.*;classWordCount{publicstaticintwords=0;publicstaticintlines=0;publicstaticintchars=0;publicstaticvoidwc(InputStreamReaderisr)throwsIOException{intc=0;booleanlastWhite=true;StringwhiteSpace="\t 42\r";while((c=isr.read())!=-1){//Countcharacterschars++;----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 43----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方//Countlinesif(c==' 44'){lines++;}//Countwordsbydetectingthestartofawordintindex=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'reworkingwithstdinwc(newInputStreamReader(System.in));}else{//We'reworkingwithalistoffilesfor(inti=0;i 45----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方拆解到被字符组界定的标记(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修改过的字数计算程序显示如下://EnhancedwordcountprogramthatusesaStreamTokenizerimportjava.io.*;classWordCount{publicstaticintwords=0;publicstaticintlines=0;publicstaticintchars=0;----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 46----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方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://FALLSTHROUGHchars+=tok.sval.length();break;}}}publicstaticvoidmain(Stringargs[]){if(args.length==0){//We'reworkingwithstdintry{wc(newInputStreamReader(System.in));System.out.println(lines+""+words+""+chars);}catch(IOExceptione){};}else{//We'reworkingwithalistoffilesinttwords=0,tchars=0,tlines=0;for(inti=0;i 47----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方)是把一个对象的状态写入一个字节流的过程。当你想要把你的程序状态存到一个固定的存储区域例如文件时,它是很管用的。稍后一点时间,你就可以运用序列化过程存储这些对象。序列化也要执行远程方法调用(RMI)。RMI允许一台机器上的Java对象调用不同机器上的Java对象方法。对象可以作为一个参数提供给那个远程方法。发送机序列化该对象并传送它。接受机反序列化它(关于RMI的更多内容请参看第24章)。假设一个被序列化的对象引用了其他对象,同样,其他对象又引用了更多的对象。这一系列的对象和它们的关系形成了一个顺序图表。在这个对象图表中也有循环引用。也就是说,对象X可以含有一个对象Y的引用,对象Y同样可以包含一个对象X的引用。对象同样可以包含它们自己的引用。对象序列化和反序列化的工具被设计出来并在这一假定条件下运行良好。如果你试图序列化一个对象图表中顶层的对象,所有的其他的引用对象都被循环的定位和序列化。同样,在反序列化过程中,所有的这些对象以及它们的引用都被正确的恢复。下面是支持序列化的接口和类的概述。17.7.1Serializable接口只有一个实现Serializable接口的对象可以被序列化工具存储和恢复。Serializable接口没有定义任何成员。它只用来表示一个类可以被序列化。如果一个类可以序列化,它的所有子类都可以序列化。声明成transient的变量不被序列化工具存储。同样,static变量也不被存储。17.7.2Externalizable接口Java的序列化和反序列化的工具被设计出来,所以很多存储和恢复对象状态的工作自动进行。然而,在某些情况下,程序员必须控制这些过程。例如,在需要使用压缩或加密技术时,Externalizable接口为这些情况而设计。Externalizable接口定义了两个方法:voidreadExternal(ObjectInputinStream) throwsIOException,ClassNotFoundExceptionvoidwriteExternal(ObjectOutputoutStream) throwsIOException这些方法中,inStream是对象被读取的字节流,outStream是对象被写入的字节流。17.7.3ObjectOutput接口ObjectOutput继承DataOutput接口并且支持对象序列化。它定义的方法显示于表17-5中。特别注意writeObject( )方法,它被称为序列化一个对象。所有这些方法在出错情况下引发IOException异常。表17-5ObjectOutput定义的方法----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 48----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方方法描述voidclose()关闭调用的流。关闭后的写操作会产生IOException异常voidflush()定制输出状态以使每个缓冲器都被清除。也就是刷新输出缓冲区voidwrite(bytebuffer[])向调用的流写入一个字节数组voidwrite(bytebuffer[],intoffset,intnumBytes)写入数组buffer中从buffer[offset]位置开始的numBytes个字节长度区域内的数据voidwrite(intb)向调用的流写入单个字节。写入的是b的低位字节voidwriteObject(Objectobj)向调用的流写入obj对象17.7.4ObjectOutputStream类ObjectOutputStream类继承OutputStream类和实现ObjectOutput接口。它负责向流写入对象。该类的构造函数如下:ObjectOutputStream(OutputStreamoutStream)throwsIOException参数outStream是序列化的对象将要写入的输出流。该类中最常用的方法列于表17-6。它们在出错情况下引发IOException异常。Java2给ObjectOuputStream增加了一个名为PutField的内部类。该类有助于持久域的编写,它的用法超出了本书的范围。表17-6ObjectOutputStream定义的常用方法方法描述voidclose()关闭调用的流。关闭后的写操作会产生IOException异常voidflush()定制输出状态以使每个缓冲器都被清除。也就是刷新输出缓冲区voidwrite(bytebuffer[])向调用的流写入一个字节数组voidwrite(bytebuffer[],intoffset,intnumBytes)写入数组buffer中从buffer[offset]位置开始的numBytes个字节长度区域内的数据voidwrite(intb)向调用的流写入单个字节。写入的是b的低位字节voidwriteBoolean(booleanb)向调用流写入一个布尔型值voidwritebyte(intb)向调用的流写入字节。写入的是b的低位字节VoidwriteBytes(Stringstr)通过str向输入流写入字节voidwriteChar(intc)向调用流写入字符型值voidwriteChars(Stringstr)通过str向调用流写入字符voidwriteDouble(doubled)向调用流写入双精度值voidwriteFloat(floatf)向调用流写入浮点数voidwriteInt(inti)向调用流写入整型数voidwriteLong(longl)向调用流写入长整型数finalvoidwriteObject(Objectobj)向调用流写入objvoidwriteShort(inti)向调用流写入short型----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 49----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方17.7.5ObjectInputObjectInput接口继承DataInput接口并且定义了表17-7中的方法。它支持对象序列化。特别注意readObject( )方法,它叫反序列化对象。所有这些方法在出错情况下引发IOException异常。表17-7ObjectInput定义的方法方法描述intavailable()返回输入缓冲区中现在可访问的字节数voidclose()关闭调用流。关闭后的读取操作会产生IOException异常intread()返回代表下一个输入字节的整数,遇到文件尾返回-1intread(bytebuffer[])试图读取buffer中的buffer.length长度的字节,返回实际成功读取的字节数,遇到文件尾返回-1续表方法描述intread(bytebuffer[],intoffset,intnumBytes)试图读取buffer中buffer[offset]为起点的numBytes个字节,返回实际成功读取的字节数。遇到文件尾时返回-1ObjectreadObject()从调用流读取一个对象。longskip(longnumBytes)忽略(跳过)调用流的numBytes个字节,返回实际忽略的字节数17.7.6ObjectInputStreamObjectInputStream继承InputStream类并实现ObjectInput接口。ObjectInputStream负责从流中读取对象。该类的构造函数如下:ObjectInputStream(InputStreaminStream) throwsIOException,StreamCorruptedException参数inStream是序列化对象将被读取的输入流。该类中最常用的方法列于表17-8。它们在出错情况下将引发IOException异常。Java2为ObjectInputStream增加了一个名为GetField的内部类。它帮助读取持久域,它的用途超出了本书的范围。并且,Java2中不被赞成的readLine( )方法不应该继续使用。表17-8ObjectInputStream定义的常用方法方法描述intavailable()返回输入缓冲中现在可访问的的字节数voidclose()关闭调用流。关闭后的读取操作会产生IOException异常intread()返回表示下一个输入字节的整数,遇到文件尾返回-1----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 50----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方intread(bytebuffer[],intoffset,intnumBytes)试图读取buffer中buffer[offset]为起点的numBytes个字节,返回实际成功读取的字节数。遇到文件尾时返回-1booleanreadBoolean()从调用流读取并返回一个boolean型值bytereadByte()从调用流读取并返回一个byte型值charreadChar()从调用流读取并返回一个char型值doublereadDouble()从调用流读取并返回一个double型值floatreadFloat()从调用流读取并返回一个float型值voidreadFully(bytebuffer[])读取buffer中buffer.length个字节。仅当所有字节被读取时会返回voidreadFully(bytebuffer[],intoffset,intnumBytes)读取buffer中从buffer[offset]开始的numBytes个字节,仅当numBytes个字节被读取时有返回intreadInt()从调用流读取并返回一个int型值longreadLong()从调用流读取并返回一个long型值finalObjectreadObject()从调用流读取并返回一个对象shortreadShort()从调用流读取并返回一个short型值intreadUnsignedByte()从调用流读取并返回一个无符号byte型值intreadUnsignedShort()从调用流读取一个无符号short型值17.7.7序列化示例下面的程序说明了怎样实现对象序列化和反序列化。它由实例化一个MyClass类的对象开始。该对象有三个实例变量,它们的类型分别是String,int和double。这是我们希望存储和恢复的信息。FileOutputStream被创建,引用了一个名为“serial”的文件。为该文件流创建一个ObjectOutputStream。ObjectOutputStream的writeObject( )方法用来序列化对象。对象的输出流被刷新和关闭。然后,引用名为“serial”的文件创建一个FileInputStream类并为该文件创建一个ObjectInputStream类。ObjectInputStream的readObject( )方法用来反序列化对象。然后对象输入流被关闭。注意MyClass被定义成实现Serializable接口。如果不这样做,将会引发一个NotSerializableException异常。试图做一些把MyClass实例变量声明成transient的实验。那些数据在序列化过程中不被保存。importjava.io.*;publicclassSerializationDemo{publicstaticvoidmain(Stringargs[]){//Objectserializationtry{MyClassobject1=newMyClass("Hello",-7,2.7e10);System.out.println("object1:"+object1);FileOutputStreamfos=newFileOutputStream("serial");ObjectOutputStreamoos=newObjectOutputStream(fos);----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 51----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方oos.writeObject(object1);oos.flush();oos.close();}catch(Exceptione){System.out.println("Exceptionduringserialization:"+e);System.exit(0);}//Objectdeserializationtry{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.7E10object2:s=Hello;i=-7;d=2.7E10----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方 52----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方17.8流的益处Java的输入输出的流式接口为复杂而繁重的任务提供了一个简洁的抽象。过滤流类的组合允许你动态建立客户端流式接口来配合数据传输要求。继承高级流类InputStream、InputStreamReader、Reader和Writer类的Java程序在将来(即使创建了新的和改进的具体类)也能得到合理运用。就像你将在下一章看到的,这种模式在我们从基于文件系统的流转换到网络和套接字流时工作良好。最后,对象序列化有望在未来的Java编程中扮演一个越来越重要的角色。Java的序列化输入/输出类为这些有时显得十分复杂的任务提供了便携的解决方法。----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方
此文档下载收益归作者所有