《《Java程序设计基础》第10章:输入输出系统.ppt》由会员分享,可在线阅读,更多相关《《Java程序设计基础》第10章:输入输出系统.ppt(48页珍藏版)》请在金锄头文库上搜索。
1、第第10章章 输入输出系统输入输出系统学习重点:学习重点:l输入输出的总体结构输入输出的总体结构 l流的概念流的概念l构建不同的流构建不同的流第第10章章 输入输出系统输入输出系统 10.110.1 输入输出流的概述输入输出流的概述 10.210.2 各种流的使用各种流的使用 10.2.110.2.1 文件流文件流 10.2.210.2.2 管道流管道流 10.2.310.2.3 连接文件连接文件 10.2.410.2.4 过滤流过滤流 10.2.510.2.5 对象的序列化对象的序列化 10.2.610.2.6 随机访问随机访问 10.310.3 练习题练习题 10.1 输入输出流的概述输入
2、输出流的概述 JavaJava的输入和输出多以流的方式进行的,它的特点是数据的发送的输入和输出多以流的方式进行的,它的特点是数据的发送和获取都是延数据序列顺序进行的,每个数据必须等待它前面的数和获取都是延数据序列顺序进行的,每个数据必须等待它前面的数据发送或读入后才能被读写。据发送或读入后才能被读写。l当需要读入数据时,程序先从数据的来源当需要读入数据时,程序先从数据的来源( (文件、网络等文件、网络等) )打开一个流,打开一个流,然后从这个流中顺序读取数据然后从这个流中顺序读取数据l当要输出数据时,程序打开一个流,通过这个流向输出目标顺序写入数当要输出数据时,程序打开一个流,通过这个流向输出
3、目标顺序写入数据据1. Character流流 CharacterCharacter流以流以Reader(Reader(对应输入对应输入) )和和Writer(Writer(对应输出对应输出) )两个类族来实现,两个类族来实现,其中其中ReaderReader和和WriterWriter是输入和输出族的根类是输入和输出族的根类 ReaderBufferedReaderCharArrayReaderInputStreamReaderFilterReaderPipedReaderStringReaderLineNumberReaderFileReaderPushbackReaderWriterBu
4、fferedwriterCharArraywriterOutputStreamReaderFilterWriterPipedWriterStringWriterFilterWriterFileWriter2. Byte流流 传输传输8 8位的数据就应用位的数据就应用 Byte流,流,JavaJava库中用库中用InputStream( (输入输入) )和和OutputStream( (输出输出) )类族中的类来实现类族中的类来实现8 8位数据的传输,这些类主要用来位数据的传输,这些类主要用来传输二进制数据,如声音和图像,传输二进制数据,如声音和图像,ObjectInputStream Obje
5、ctOutputStreamyObjectOutputStreamy用来传输对象序列。用来传输对象序列。 3. 3. 关于关于IOIO的根类的根类Reader含有以下读取字符和字符数组的方法含有以下读取字符和字符数组的方法:int read()int read(char cbuf)int read(char cbuf, int offset, int length)而而InputStream定义了读取定义了读取bytebyte型数据的方法如下:型数据的方法如下:int read()int read(byte cbuf)int read(byte cbuf, int offset, int le
6、ngth)Writer方法如下:方法如下:int write(int c)int write(char cbuf)int write(char cbuf, int offset, int length)OutputStream方法如下:方法如下:int write(int c)int write(byte cbuf)int write(byte cbuf, int offset, int length)4. 4. 各种流简介各种流简介表表10.110.1列出了列出了java.io包中的各种流和它们的功能。注意,这些流都能传输包中的各种流和它们的功能。注意,这些流都能传输char和和byteby
7、te,两种不同的数据类型。,两种不同的数据类型。 l表表10.110.110.2 各种流的使用各种流的使用10.2.1 文件流文件流 文件流文件流( (File streams) )是用来传输当前系统下的某个文件中的一些内容是用来传输当前系统下的某个文件中的一些内容的,它应该是最简单的一种流,它可以是以下几种流类的对象:的,它应该是最简单的一种流,它可以是以下几种流类的对象:FileReader,FileWriter,FileInputStream和和FileOutputStream。 例例10.1 10.1 使用使用File Reader和和File writer的文件复制的文件复制 这个例
8、子就是把这个例子就是把partnovel.txt的内容传输到的内容传输到target.txt中,这两个中,这两个文件都在本机的文件都在本机的e:files中。中。l程序代码程序代码例例10.2 10.2 使用使用InputStream和和OutputStream的文件复制的文件复制l程序代码程序代码l两个方法复制同样一段文件内容,每次读取的内容是不一样的,两个方法复制同样一段文件内容,每次读取的内容是不一样的,FileReader每次读取的是一个字符每次读取的是一个字符( (charactor) ),而屏幕中显示的是这个,而屏幕中显示的是这个字符的编码字符的编码( (0到到65 535之间的一
9、个整数之间的一个整数) )。而。而FileInputStream每次读取的每次读取的是一个字节是一个字节( (byte) ),而屏幕中显示的是这个字节的编码,而屏幕中显示的是这个字节的编码( (0255之间的一之间的一个整数个整数) )。10.2.2 管道流管道流 管道流管道流( (Pipe Streams) )是把一个线程的输出作为另一个线程的输是把一个线程的输出作为另一个线程的输入。实现它的是入。实现它的是PipedReader、PipedWriter、PipedInputStream和和PipedOutputStream。管道流管道流(Pipe Streams)(Pipe Streams
10、)的作用的作用 如果定义了一个类,用来实现对一组词的操作,其中的一个操作是按它如果定义了一个类,用来实现对一组词的操作,其中的一个操作是按它们的韵们的韵( (词尾词尾) )排序,方法是先把这些词的字序逆转排序,方法是先把这些词的字序逆转( (reverse(),然后把逆,然后把逆转转后的词排序后的词排序( (sort(),最后再逆转每个词,最后再逆转每个词( (reverse(),这样就得到这些词,这样就得到这些词的的韵的排序。韵的排序。 如果不用管道流,这个操作过程必须存储两个中间过程,即经过第如果不用管道流,这个操作过程必须存储两个中间过程,即经过第一次一次reverse()reverse
11、()后得到的词表和经过后得到的词表和经过sort()sort()之后的词表。如图所示之后的词表。如图所示ReverseReverseSortList ofWordsList ofReversedWordsList ofReversedSorted WordsList ofRhymingWords而如果用管道流,把一个方法的输出作为另一个方法的输入,就不而如果用管道流,把一个方法的输出作为另一个方法的输入,就不需要中间的存储文件了,当然这时必须用多个线程同时运行,即需要中间的存储文件了,当然这时必须用多个线程同时运行,即reversrevers()(),sort()sort()和和reverse
12、()reverse()一起工作,并且把中间的存储文件用管道流一起工作,并且把中间的存储文件用管道流来代替。如图所示。来代替。如图所示。List ofWordsList ofRhymingWordsReverseReverseSort例例10.3 对词汇的韵排序对词汇的韵排序 这个例子中一共定义了这个例子中一共定义了3 3个类,主要的流程结构定义在个类,主要的流程结构定义在RhymingWordsRhymingWords类类中,它是这个程序的主类,另外,我们还定义了中,它是这个程序的主类,另外,我们还定义了ReverseThreadReverseThread和和SortThreadSortThr
13、ead两个线程,它们的工作就是分别执行上图中指出的两个线程,它们的工作就是分别执行上图中指出的reversereverse和和sortsort的动作,的动作, (1)ReverseThread的作用是执行将单词的字母顺序逆转过来的的作用是执行将单词的字母顺序逆转过来的动作,源代码如下:动作,源代码如下:l程序代码程序代码l这个线程对读入的每一行数据调用了这个线程对读入的每一行数据调用了reverseIt()方法,并将逆转方法,并将逆转完毕的单词输出到一个完毕的单词输出到一个OutputStream类对象中去。注意,在这段类对象中去。注意,在这段程序中,我们只使用了普通的输入输出流。程序中,我们
14、只使用了普通的输入输出流。(2) SortThread的作用是对单词进行排序,其源代码如下:的作用是对单词进行排序,其源代码如下:l程序代码程序代码(3)(3)RhymingWords类控制着整个程序的流程类控制着整个程序的流程:l程序代码程序代码对于管道流的使用主要体现在粗体的代码段,如对于管道流的使用主要体现在粗体的代码段,如reverse()reverse()方法中的方法中的语句:语句:PipedWriter pipeOut = new PipedWriter();PipedReader pipeIn = new PipedReader(pipeOut);l以上的两句作用是建立一个管道,
15、管道的一头是PipedWriter,另一头是PipedReader,并且,任何从PipedWriter写入的内容都可以从PipedReader读出。形成这个管道的过程就是在一个PipedReader上建立一个PipedWriter。管道流和文件流的主要区别是文件流必须建立在一个文件上,而管道流是在两个线程之间之间建立管道,而不是建立在某个文件或线程上。所以,管道流的建立过程是先创建一个空的PipedWriter,然后在PipedWriter上创建PioedReader。程序运行时Reverse线程把内容输入到管道的PipedWriter端,Sort线程从管道的PipedReader端读出如图所
16、示。sort()方法中的管道流同样,只是使用管道的线程不同而已。PipedwriterPipedReaberReversebortThe Pipel管道的连接管道的连接PipedwriterPipedReaberReversebortThe Pipe快排序的算法快排序的算法 /这是一个快排序的方法,它的思路是先设一个中间点,然后通过左右对调/把值小于中间点的元素放到中间点的左边,值大于中间点的元素放到右边/然后对左右两部分重复以上算法,直到完成排序,所以这是一个递归算法假设Words.txt文件中的内容如下:innewBufferedWritersourcePipepipeOutPipedWr
17、iterhiflowerairplanecomputernetworkstoppcgamenewl程序代码程序代码输出结果如图所示输出结果如图所示10.2.3 连接文件连接文件 如果需要读取多个文件,并把它们连接在一起,就需要流类如果需要读取多个文件,并把它们连接在一起,就需要流类SequenceInputStream。 例例10.4 10.4 用一个流读取多个文件并连接用一个流读取多个文件并连接l程序首先创建一个程序首先创建一个ListOfFilesListOfFiles类的对象类的对象myListmyList来存放命令行输来存放命令行输入的多个文件名,然后创建一个入的多个文件名,然后创建一
18、个SequenceInputStreamSequenceInputStream对象,它对象,它将按将按myListmyList指示的顺序读取多个文件并将它们连接。指示的顺序读取多个文件并将它们连接。l程序代码程序代码10.2.4 过滤流过滤流 java.io包中提供了一个类族,这些类实现过滤输入输出,这些类的根类包中提供了一个类族,这些类实现过滤输入输出,这些类的根类是是FilterInputStream和和FilterOutputStream,它们是抽象类。当使用过滤,它们是抽象类。当使用过滤流时,比一般流多一道工序,就是过滤。过滤流是建筑在其他流之上的,流时,比一般流多一道工序,就是过滤。
19、过滤流是建筑在其他流之上的,如过滤流的方法如过滤流的方法read()从下层流中读取数据,并过滤后传给程序,而从下层流中读取数据,并过滤后传给程序,而write()方法是先过滤后,再把数据写入下层流。方法是先过滤后,再把数据写入下层流。 FilterInputStream和和FilterOutputStream的子类如下:的子类如下:DataInputStream 和和 DataOutputStream BufferedInputStream 和和 BufferedOutputStream LineNumberInputStream PushbackInputStream PrintStream
20、 1. 1. 使用过滤流使用过滤流 要使用过滤流必须使它附加在其他流上,可以在一个标准的输入要使用过滤流必须使它附加在其他流上,可以在一个标准的输入流上附加一个过滤输入流,例如:流上附加一个过滤输入流,例如:BufferedReader d = new BufferedReader(new DataInputStream(System.in);String input;while (input = d.readLine() != null) 例例10.5 使用使用DataInputStream和和DataOutputStream进行过滤输入进行过滤输入输出输出 这个程序的结构是,首先给出一系列
21、数据,然后把这些数据通过这个程序的结构是,首先给出一系列数据,然后把这些数据通过过滤流输出到一个文件中,最后再从文件中读到屏幕上。过滤流输出到一个文件中,最后再从文件中读到屏幕上。l程序代码程序代码 2. 2. 定义自己的过滤流定义自己的过滤流 许多时候我们需要特殊的过滤方式,而在许多时候我们需要特殊的过滤方式,而在JavaJava类库中没有所需的类库中没有所需的过滤流,这时就必须自己定义。定义自己的过滤流应注意以下几过滤流,这时就必须自己定义。定义自己的过滤流应注意以下几点:点:l过滤流应该是DataInputStream和DataOutputStream的子类,而且经过过滤的数据其他方法不
22、能读出。一般情况下,输入和输出是成对出现的,所以一次定义两个流(输入、输出)。l如果需要,重载read()和write()。l可以定义其他的特殊方法完成过滤。l确保输入和输出流一起工作。例例10.6 10.6 创建自己的过滤流创建自己的过滤流l这个例子的名字为CheckSum,其作用是判断从输入流中读入的数据是否与从输出流中写入的数据相符,通过这个程序,可以实现在IO方面的多种检查算法,以保证输入流与输出流之间的一致,类似的程序在网络管理软件中经常会用到。 l这个例子一共定义了4个类和一个接口,它们分别是:过滤流的两个子类:CheckedOutputStream和CheckedInputStr
23、eam。CheckSum 接口以及实现这个接口的类Adler32。CheckedIODemo 类用于为这个程序定义main函数。(1)CheckedOutputStream类的源代码如下:类的源代码如下:l程序代码程序代码在这个类中,请注意以下几点:在这个类中,请注意以下几点:l首先继承了FilterOutputStream类,这是自定义过滤流必要的第一步。l然后定义了它的构造器,这个类的构造器只有一个,其使用的参数包括一个OutputStream类变量,这个变量即为该过滤流要过滤的输出流,另外一个参数是这个过滤流的私有变量,它是CheckSum类型的,其作用是用来更新程序的检 查和。lFil
24、terOutputStream中定义了3个不同的write()方法,这里CheckedOutputStream将这3个方法都做了重载,每次调用write()方法时,它都先写入数据,然后进行检查并更新检查和。(2)CheckedInputStream类的源代码如下:类的源代码如下:l程序代码程序代码l这个类与上面的CheckedOutputStream基本上是类似的,它继承了FilterInputStream,其构造器也只有一个,其中的参数是需要过滤的输入流和一个CheckSum类的私有变量,用来标识检查和,并且也将FilterInputStream的3个read()方法都进行了重载,重载的方法
25、就是先读入数据,再进行检查。(3)CheckSum接口的源代码如下:接口的源代码如下:interface Checksum public void update(int b); public void update(byte b, int off, int len); public long getValue(); public void reset();下面的类来具体实现了检查动作。下面的类来具体实现了检查动作。 (4)Adler32类的源代码如下:类的源代码如下:l程序代码程序代码 这个类使用了CRC-32(循环冗余)算法对输入、输出流进行检查,我们不用关心这段程序每个方法内部程序是什么意
26、思,只要注意一下,Adler32类实现了CheckSum接口的所有方法就可以了。(5)CheckedIODemo类的源代码类的源代码 除了上面介绍的几个类和接口之外,还有这样一些程序段,其作用是控制整个程序的流程,源代码如下:l程序代码程序代码假设假设farrago.txtfarrago.txt文件中存有下面一些内容:文件中存有下面一些内容:So she went into the garden to cut a cabbage-leaf, tomake an apple-pie; and at the same time a greatshe-bear, coming up the stre
27、et, pops its head into theshop. What! no soap? So he died, and she veryimprudently married the barber; and there werepresent the Picninnies, and the Joblillies, and theGaryalies, and the grand Panjandrum himself, with thelittle round button at top, and they all fell to playingthe game of catch as ca
28、tch can, till the gun powder ranout at the heels of their boots.Samuel Foote 1720-1777我们将得到如下运行结果:我们将得到如下运行结果:Input stream check sum: 736868089Output stream check sum: 73686808910.2.5 对象的序列化对象的序列化1. 1. 序列化对象序列化对象 ObjectInputStream和和ObjectOutputStream必须建筑在其他流类的基础必须建筑在其他流类的基础上,这和过滤流相似。如果把一个对象看做一个活动房屋,
29、上,这和过滤流相似。如果把一个对象看做一个活动房屋,ObjectOutputStream和和ObjectInputStream就是把房屋拆散和重新组装,当就是把房屋拆散和重新组装,当然这个过程是有一定顺序的,而其他的流类就负责零件的运输。我们先看然这个过程是有一定顺序的,而其他的流类就负责零件的运输。我们先看ObjectOutputStreamObjectOutputStream,这是拆的过程。,这是拆的过程。例例10.7 对象序列化对象序列化l程序代码程序代码l这个例子中的ObjectOutputStream和ObjectInputStream都是建立在FileOutputStream和Fi
30、leInputStream之上的(粗体部分),然后通过sOut和sIn的方法writeObject()和readObject()来输入和输出对象,这里被传输的对象是字符串“thetime:”和Date类实例。lObjectOutputStream实现了接口DataOutput,这个接口定义了许多输出简单数据的方法,如writeInt(),writeFloat()和writeUTF等,读者可以用这些方法直接把简单数据写入ObjectOutputStream。与之对应ObjectInputStream有多个读入简单数据的方法,如readInt(),readFloat()等等。2. 定义能序列化的类
31、定义能序列化的类 一个类只有实现了接口一个类只有实现了接口SerializableSerializable,它的对象才能被序列化。这听起,它的对象才能被序列化。这听起来很麻烦,事实上,来很麻烦,事实上,SerializableSerializable接口是个空接口,它不含有任何方法,接口是个空接口,它不含有任何方法,下面就是这个接口的定义:下面就是这个接口的定义:package java.io; public interface Serializable ; /很幸运,括号内没有任何东西 这样我们要使某个类的对象可被序列化,只需要在这个类的类头声明实这样我们要使某个类的对象可被序列化,只需要在
32、这个类的类头声明实现接口现接口SerializableSerializable,而它的其他定义不用进行丝毫改动,例如:,而它的其他定义不用进行丝毫改动,例如:public class MySerializableClass implements Serializable 如何对对象进行序列化一般不需要编程者自己定义如何对对象进行序列化一般不需要编程者自己定义ldefaultWriteObject()方法来处理这件事ldefaultReadObject()方法类重组对象 这两个方法分别被这两个方法分别被writeObject()和和readObject()及其他方法调用。默认的序列化及其他方法调
33、用。默认的序列化方法比较慢。方法比较慢。如果需要还是可以在序列化之后进行其他操作,这就要重载如果需要还是可以在序列化之后进行其他操作,这就要重载writeObjectwriteObject()()和和readObjectreadObject()()。但调用默认的序列化方法必须放在第一句,如。但调用默认的序列化方法必须放在第一句,如同下面的格式:同下面的格式:private void writeObject(ObjectOutputStream s) throws IOException s.defaultWriteObject(); /自定义部分private void readObject(
34、ObjectInputStream s)throws IOException, ClassNotFoundExceptions.defaultReadObject(); /自定义部分10.2.6 10.2.6 随机访问随机访问 首先我们看一下为什么需要随机访问。假如有一个首先我们看一下为什么需要随机访问。假如有一个ZIPZIP文档,大文档,大家知道这是一个压缩文档,里面可能包含多个文件,在家知道这是一个压缩文档,里面可能包含多个文件,在ZIPZIP文档的文档的最后由各个文件的索引。它的结构如图最后由各个文件的索引。它的结构如图10.1110.11所示。所示。file infofilefile
35、contentsdir-entry当我们需要取出其中的一个文件时,如果用顺序访问必须经过下面当我们需要取出其中的一个文件时,如果用顺序访问必须经过下面4 4个步骤:个步骤:(1)(1)打开打开ZIPZIP文档。文档。(2)(2)顺序查找这个文档,直到找到所需要的文件。顺序查找这个文档,直到找到所需要的文件。(3)(3)解压缩这个文件。解压缩这个文件。(4)(4)关闭关闭ZIPZIP文档。文档。 可以看出,如果用这种算法,当找到所需文件时平均需要读取半个文可以看出,如果用这种算法,当找到所需文件时平均需要读取半个文档,所以这个算法的效率是很低的。但如果用随机访问方法就不会这样,档,所以这个算法的
36、效率是很低的。但如果用随机访问方法就不会这样,我们只需找到文档最后的索引,然后根据索引找到文件的位置,直接解压我们只需找到文档最后的索引,然后根据索引找到文件的位置,直接解压缩文件,然后关闭缩文件,然后关闭ZIPZIP文档。文档。lRandomAccessFile类和前面的输入输出流不同,它把输入输出放到一个类中,通过其不同的构造函数来确定是输出还是输入。并且它是一个独立分支的类,并不是InputStream或OutputStream的衍生类lRandomAccessFile类可以随机访问本机的某个文件,只要必须提供这个文件的名称或代表这个文件的File类对象,这点和FileInputStre
37、am、FileOutputStream相似。下面是创建随机访问的实例,构造函数中的第二个参数只有“r”(只读)或“rw”(可读写)。new RandomAccessFile(partnovel.txt, r); /创建一个对文件partnovel的只读随机访问new RandomAccessFile(farrago.txt, rw); /创建一个对文件farrago的可读写随机访问 一旦建立了对文件的随机访问,就可以使用read()和write()方法了,如readInt(),readFloat(),writeInt()等等。下面是下面是3个操纵指针的方法:个操纵指针的方法:lint skip
38、Bytes(int) /让指针移动跳过形参指示的字数,形参单位是bytelvoid seek(long) /把指针从起始位移动到形参指示位置,形参单位是byte llong getFilePointer() /获取当前指针位置(相对于起始位) 例例10.8 10.8 使用随机访问类使用随机访问类 这个例子就是用来对这个例子就是用来对partnovel.txtpartnovel.txt进行随机读取,其中用进行随机读取,其中用n n表示表示从文件头开始忽略的多少字。从文件头开始忽略的多少字。l程序代码程序代码10.3 10.3 练练 习习 题题1. 1. 选择题选择题(1)(1)Character
39、Character流与流与ByteByte流的区别在于:流的区别在于:A.A. 每次读入的字节数不同每次读入的字节数不同B.B. 前者带有缓冲,后者没有前者带有缓冲,后者没有C.C. 前者是块读写,后者是字节读写前者是块读写,后者是字节读写D.D. 二者没有区别,可以互换使用二者没有区别,可以互换使用(2)(2)如果要读取一个大文件的末尾的一段内容,并且知道该段落的确切位置,如果要读取一个大文件的末尾的一段内容,并且知道该段落的确切位置,最方便的流是:最方便的流是: A. File stream B. Piped stream C. Random access stream D. Filter
40、 stream2. 2. 程序阅读题程序阅读题下述程序段的执行效率是否良好,如何修改能够提高?下述程序段的执行效率是否良好,如何修改能够提高?int i;URL url = new URL(http:/ javaSite = url.openConnection();InputStream input = javaSite.getInputStream();InputStreamReader reader = new InputStreamReader(input);while (i = reader.read() != -1) System.out.print(i);3. 编程题编程题(1)
41、(1)把一些数据加到一个文件的末尾。把一些数据加到一个文件的末尾。(2)(2)用用PushbackInputStreamPushbackInputStream或或PushbackReaderPushbackReader实现逐词读取一个文件的内实现逐词读取一个文件的内容容( (用这两个类来提前检查空格,以确定一个词用这两个类来提前检查空格,以确定一个词) )。(3)(3)实现一对实现一对ReaderReader和和WriterWriter,给输入、输出特殊的字母计数,如输出的文,给输入、输出特殊的字母计数,如输出的文件中有多少个件中有多少个a a,这个字母必须容易更改。,这个字母必须容易更改。(4)(4)实现一个可序列化的对象并进行传输。实现一个可序列化的对象并进行传输。