资源描述
第10章输入输出系统,学习重点:输入输出的总体结构流的概念构建不同的流,第10章输入输出系统,10.1输入输出流的概述10.2各种流的使用10.2.1文件流10.2.2管道流10.2.3连接文件10.2.4过滤流10.2.5对象的序列化10.2.6随机访问10.3练习题,10.1输入输出流的概述,Java的输入和输出多以流的方式进行的,它的特点是数据的发送和获取都是延数据序列顺序进行的,每个数据必须等待它前面的数据发送或读入后才能被读写。,当需要读入数据时,程序先从数据的来源(文件、网络等)打开一个流,然后从这个流中顺序读取数据当要输出数据时,程序打开一个流,通过这个流向输出目标顺序写入数据,1.Character流,Character流以Reader(对应输入)和Writer(对应输出)两个类族来实现,其中Reader和Writer是输入和输出族的根类,2.Byte流,传输8位的数据就应用Byte流,Java库中用InputStream(输入)和OutputStream(输出)类族中的类来实现8位数据的传输,这些类主要用来传输二进制数据,如声音和图像,ObjectInputStreamObjectOutputStreamy用来传输对象序列。,3.关于IO的根类,Reader含有以下读取字符和字符数组的方法:intread()intread(charcbuf)intread(charcbuf,intoffset,intlength)而InputStream定义了读取byte型数据的方法如下:intread()intread(bytecbuf)intread(bytecbuf,intoffset,intlength),Writer方法如下:,intwrite(intc)intwrite(charcbuf)intwrite(charcbuf,intoffset,intlength)OutputStream方法如下:intwrite(intc)intwrite(bytecbuf)intwrite(bytecbuf,intoffset,intlength),4.各种流简介,表10.1列出了java.io包中的各种流和它们的功能。注意,这些流都能传输char和byte,两种不同的数据类型。表10.1,10.2各种流的使用,10.2.1文件流文件流(Filestreams)是用来传输当前系统下的某个文件中的一些内容的,它应该是最简单的一种流,它可以是以下几种流类的对象:FileReader,FileWriter,FileInputStream和FileOutputStream。,例10.1使用FileReader和Filewriter的文件复制,这个例子就是把partnovel.txt的内容传输到target.txt中,这两个文件都在本机的e:files中。程序代码,例10.2使用InputStream和OutputStream的文件复制,程序代码两个方法复制同样一段文件内容,每次读取的内容是不一样的,FileReader每次读取的是一个字符(charactor),而屏幕中显示的是这个字符的编码(0到65535之间的一个整数)。而FileInputStream每次读取的是一个字节(byte),而屏幕中显示的是这个字节的编码(0255之间的一个整数)。,10.2.2管道流,管道流(PipeStreams)是把一个线程的输出作为另一个线程的输入。实现它的是PipedReader、PipedWriter、PipedInputStream和PipedOutputStream。,管道流(PipeStreams)的作用,如果定义了一个类,用来实现对一组词的操作,其中的一个操作是按它们的韵(词尾)排序,方法是先把这些词的字序逆转(reverse(),然后把逆转后的词排序(sort(),最后再逆转每个词(reverse(),这样就得到这些词的韵的排序。,如果不用管道流,这个操作过程必须存储两个中间过程,即经过第一次reverse()后得到的词表和经过sort()之后的词表。如图所示,Reverse,Reverse,Sort,ListofWords,ListofReversedWords,ListofReversedSortedWords,ListofRhymingWords,而如果用管道流,把一个方法的输出作为另一个方法的输入,就不需要中间的存储文件了,当然这时必须用多个线程同时运行,即revers(),sort()和reverse()一起工作,并且把中间的存储文件用管道流来代替。如图所示。,ListofWords,ListofRhymingWords,例10.3对词汇的韵排序,这个例子中一共定义了3个类,主要的流程结构定义在RhymingWords类中,它是这个程序的主类,另外,我们还定义了ReverseThread和SortThread两个线程,它们的工作就是分别执行上图中指出的reverse和sort的动作,,(1)ReverseThread的作用是执行将单词的字母顺序逆转过来的动作,源代码如下:,程序代码这个线程对读入的每一行数据调用了reverseIt()方法,并将逆转完毕的单词输出到一个OutputStream类对象中去。注意,在这段程序中,我们只使用了普通的输入输出流。,(2)SortThread的作用是对单词进行排序,其源代码如下:程序代码(3)RhymingWords类控制着整个程序的流程:程序代码,对于管道流的使用主要体现在粗体的代码段,如reverse()方法中的语句:,PipedWriterpipeOut=newPipedWriter();PipedReaderpipeIn=newPipedReader(pipeOut);以上的两句作用是建立一个管道,管道的一头是PipedWriter,另一头是PipedReader,并且,任何从PipedWriter写入的内容都可以从PipedReader读出。形成这个管道的过程就是在一个PipedReader上建立一个PipedWriter。管道流和文件流的主要区别是文件流必须建立在一个文件上,而管道流是在两个线程之间建立管道,而不是建立在某个文件或线程上。所以,管道流的建立过程是先创建一个空的PipedWriter,然后在PipedWriter上创建PioedReader。程序运行时Reverse线程把内容输入到管道的PipedWriter端,Sort线程从管道的PipedReader端读出如图所示。sort()方法中的管道流同样,只是使用管道的线程不同而已。PipedwriterPipedReaberReversebortThePipe管道的连接,快排序的算法,/这是一个快排序的方法,它的思路是先设一个中间点,然后通过左右对调/把值小于中间点的元素放到中间点的左边,值大于中间点的元素放到右边/然后对左右两部分重复以上算法,直到完成排序,所以这是一个递归算法假设Words.txt文件中的内容如下:innewBufferedWritersourcePipepipeOutPipedWriterhiflowerairplanecomputernetworkstoppcgamenew,程序代码,输出结果如图所示,10.2.3连接文件,如果需要读取多个文件,并把它们连接在一起,就需要流类SequenceInputStream。,例10.4用一个流读取多个文件并连接,程序首先创建一个ListOfFiles类的对象myList来存放命令行输入的多个文件名,然后创建一个SequenceInputStream对象,它将按myList指示的顺序读取多个文件并将它们连接。程序代码,10.2.4过滤流,java.io包中提供了一个类族,这些类实现过滤输入输出,这些类的根类是FilterInputStream和FilterOutputStream,它们是抽象类。当使用过滤流时,比一般流多一道工序,就是过滤。过滤流是建筑在其他流之上的,如过滤流的方法read()从下层流中读取数据,并过滤后传给程序,而write()方法是先过滤后,再把数据写入下层流。,FilterInputStream和FilterOutputStream的子类如下:,DataInputStream和DataOutputStreamBufferedInputStream和BufferedOutputStreamLineNumberInputStreamPushbackInputStreamPrintStream,1.使用过滤流,要使用过滤流必须使它附加在其他流上,可以在一个标准的输入流上附加一个过滤输入流,例如:BufferedReaderd=newBufferedReader(newDataInputStream(System.in);Stringinput;while(input=d.readLine()!=null),例10.5使用DataInputStream和DataOutputStream进行过滤输入输出,这个程序的结构是,首先给出一系列数据,然后把这些数据通过过滤流输出到一个文件中,最后再从文件中读到屏幕上。程序代码,2.定义自己的过滤流,许多时候我们需要特殊的过滤方式,而在Java类库中没有所需的过滤流,这时就必须自己定义。定义自己的过滤流应注意以下几点:过滤流应该是DataInputStream和DataOutputStream的子类,而且经过过滤的数据其他方法不能读出。一般情况下,输入和输出是成对出现的,所以一次定义两个流(输入、输出)。如果需要,重载read()和write()。可以定义其他的特殊方法完成过滤。确保输入和输出流一起工作。,例10.6创建自己的过滤流,这个例子的名字为CheckSum,其作用是判断从输入流中读入的数据是否与从输出流中写入的数据相符,通过这个程序,可以实现在IO方面的多种检查算法,以保证输入流与输出流之间的一致,类似的程序在网络管理软件中经常会用到。这个例子一共定义了4个类和一个接口,它们分别是:过滤流的两个子类:CheckedOutputStream和CheckedInputStream。CheckSum接口以及实现这个接口的类Adler32。CheckedIODemo类用于为这个程序定义main函数。,(1)CheckedOutputStream类的源代码如下:,程序代码在这个类中,请注意以下几点:首先继承了FilterOutputStream类,这是自定义过滤流必要的第一步。然后定义了它的构造器,这个类的构造器只有一个,其使用的参数包括一个OutputStream类变量,这个变量即为该过滤流要过滤的输出流,另外一个参数是这个过滤流的私有变量,它是CheckSum类型的,其作用是用来更新程序的检查和。FilterOutputStream中定义了3个不同的write()方法,这里CheckedOutputStream将这3个方法都做了重载,每次调用write()方法时,它都先写入数据,然后进行检查并更新检查和。,(2)CheckedInputStream类的源代码如下:,程序代码这个类与上面的CheckedOutputStream基本上是类似的,它继承了FilterInputStream,其构造器也只有一个,其中的参数是需要过滤的输入流和一个CheckSum类的私有变量,用来标识检查和,并且也将FilterInputStream的3个read()方法都进行了重载,重载的方法就是先读入数据,再进行检查。,(3)CheckSum接口的源代码如下:,interfaceChecksumpublicvoidupdate(intb);publicvoidupdate(byteb,intoff,intlen);publiclonggetValue();publicvoidreset();,下面的类来具体实现了检查动作。,(4)Adler32类的源代码如下:程序代码这个类使用了CRC-32(循环冗余)算法对输入、输出流进行检查,我们不用关心这段程序每个方法内部程序是什么意思,只要注意一下,Adler32类实现了CheckSum接口的所有方法就可以了。,(5)CheckedIODemo类的源代码,除了上面介绍的几个类和接口之外,还有这样一些程序段,其作用是控制整个程序的流程,源代码如下:程序代码,假设farrago.txt文件中存有下面一些内容:,Soshewentintothegardentocutacabbage-leaf,tomakeanapple-pie;andatthesametimeagreatshe-bear,comingupthestreet,popsitsheadintotheshop.What!nosoap?Sohedied,andsheveryimprudentlymarriedthebarber;andtherewerepresentthePicninnies,andtheJoblillies,andtheGaryalies,andthegrandPanjandrumhimself,withthelittleroundbuttonattop,andtheyallfelltoplayingthegameofcatchascatchcan,tillthegunpowderranoutattheheelsoftheirboots.SamuelFoote1720-1777我们将得到如下运行结果:Inputstreamchecksum:736868089Outputstreamchecksum:736868089,10.2.5对象的序列化,1.序列化对象ObjectInputStream和ObjectOutputStream必须建筑在其他流类的基础上,这和过滤流相似。如果把一个对象看做一个活动房屋,ObjectOutputStream和ObjectInputStream就是把房屋拆散和重新组装,当然这个过程是有一定顺序的,而其他的流类就负责零件的运输。我们先看ObjectOutputStream,这是拆的过程。,例10.7对象序列化,程序代码这个例子中的ObjectOutputStream和ObjectInputStream都是建立在FileOutputStream和FileInputStream之上的(粗体部分),然后通过sOut和sIn的方法writeObject()和readObject()来输入和输出对象,这里被传输的对象是字符串“thetime:”和Date类实例。ObjectOutputStream实现了接口DataOutput,这个接口定义了许多输出简单数据的方法,如writeInt(),writeFloat()和writeUTF等,读者可以用这些方法直接把简单数据写入ObjectOutputStream。与之对应ObjectInputStream有多个读入简单数据的方法,如readInt(),readFloat()等等。,2.定义能序列化的类,一个类只有实现了接口Serializable,它的对象才能被序列化。这听起来很麻烦,事实上,Serializable接口是个空接口,它不含有任何方法,下面就是这个接口的定义:packagejava.io;publicinterfaceSerializable;/很幸运,括号内没有任何东西这样我们要使某个类的对象可被序列化,只需要在这个类的类头声明实现接口Serializable,而它的其他定义不用进行丝毫改动,例如:publicclassMySerializableClassimplementsSerializable如何对对象进行序列化一般不需要编程者自己定义defaultWriteObject()方法来处理这件事defaultReadObject()方法类重组对象这两个方法分别被writeObject()和readObject()及其他方法调用。默认的序列化方法比较慢。,如果需要还是可以在序列化之后进行其他操作,这就要重载writeObject()和readObject()。但调用默认的序列化方法必须放在第一句,如同下面的格式:,privatevoidwriteObject(ObjectOutputStreams)throwsIOExceptions.defaultWriteObject();/自定义部分privatevoidreadObject(ObjectInputStreams)throwsIOException,ClassNotFoundExceptions.defaultReadObject();/自定义部分,10.2.6随机访问,首先我们看一下为什么需要随机访问。假如有一个ZIP文档,大家知道这是一个压缩文档,里面可能包含多个文件,在ZIP文档的最后由各个文件的索引。它的结构如图10.11所示。,当我们需要取出其中的一个文件时,如果用顺序访问必须经过下面4个步骤:,(1)打开ZIP文档。(2)顺序查找这个文档,直到找到所需要的文件。(3)解压缩这个文件。(4)关闭ZIP文档。可以看出,如果用这种算法,当找到所需文件时平均需要读取半个文档,所以这个算法的效率是很低的。但如果用随机访问方法就不会这样,我们只需找到文档最后的索引,然后根据索引找到文件的位置,直接解压缩文件,然后关闭ZIP文档。,RandomAccessFile类和前面的输入输出流不同,它把输入输出放到一个类中,通过其不同的构造函数来确定是输出还是输入。并且它是一个独立分支的类,并不是InputStream或OutputStream的衍生类RandomAccessFile类可以随机访问本机的某个文件,只要必须提供这个文件的名称或代表这个文件的File类对象,这点和FileInputStream、FileOutputStream相似。下面是创建随机访问的实例,构造函数中的第二个参数只有“r”(只读)或“rw”(可读写)。newRandomAccessFile(partnovel.txt,r);/创建一个对文件partnovel的只读随机访问newRandomAccessFile(farrago.txt,rw);/创建一个对文件farrago的可读写随机访问一旦建立了对文件的随机访问,就可以使用read()和write()方法了,如readInt(),readFloat(),writeInt()等等。,下面是3个操纵指针的方法:,intskipBytes(int)/让指针移动跳过形参指示的字数,形参单位是bytevoidseek(long)/把指针从起始位移动到形参指示位置,形参单位是bytelonggetFilePointer()/获取当前指针位置(相对于起始位),例10.8使用随机访问类,这个例子就是用来对partnovel.txt进行随机读取,其中用n表示从文件头开始忽略的多少字。程序代码,10.3练习题,1.选择题(1)Character流与Byte流的区别在于:A.每次读入的字节数不同B.前者带有缓冲,后者没有C.前者是块读写,后者是字节读写D.二者没有区别,可以互换使用,(2)如果要读取一个大文件的末尾的一段内容,并且知道该段落的确切位置,最方便的流是:A.FilestreamB.PipedstreamC.RandomaccessstreamD.Filterstream,2.程序阅读题,下述程序段的执行效率是否良好,如何修改能够提高?inti;URLurl=newURL(,3.编程题,(1)把一些数据加到一个文件的末尾。(2)用PushbackInputStream或PushbackReader实现逐词读取一个文件的内容(用这两个类来提前检查空格,以确定一个词)。(3)实现一对Reader和Writer,给输入、输出特殊的字母计数,如输出的文件中有多少个a,这个字母必须容易更改。(4)实现一个可序列化的对象并进行传输。,
展开阅读全文