第 12章 Java I/O处理
对于任何程序设计语言而言,输入输出( I/O)系统是最复杂的一部分,因为通信的双方不仅仅是
I/O源端和接收端,还可能是文件、网络链接或内存磁盘等,而且这些数据的数据格式多样,如字符、二进制、字节。 Java通过创建大量的类库解决这个问题。
12.1 流( stream)的概念
Java的 I/O系统涉及流的概念。一个读取字节序列的对象被称为输入流,一个可以写入字节序列的对象称为输出流。输出流和输入流是相对于程序本身而言的。程序读取数据称为打开输入流,程序向其他源写入数据称为打开输出流,该过程如图所示。
12.2 字符流
在 Java的 I/O系统提供了 InputStream和
OutputStream两个抽象类实现字节( 8位)数据的输入输出,其中 InputStream是输入流的抽象类,
提供了 read方法,各个实现了该类的子类都要实现该方法,如 ObjectInputStream类继承
InputStream抽象类,重新定义了方法 read()来读取字节数据。本节介绍抽象类 InputStream和
OutputStream及其相对应的子类。
12.2.1 输入流类 InputStream
抽象类 InputStream表示从不同的输入源输入数据的类,这些数据源的数据类型多样,可以是字节数组,String对象,类的序列化对象,文件,管道或网络链接 。 对于多样的数据类型有相应的输入流类与其对应 。 下面介绍这些流类,使读者对这些类的功能和使用方式有基本的了解 。
InputStream是个抽象类,提供了抽象 read方法,下面几个类是继承自
InputStream的子类:
ByteArrayInputStream( 字节数组输入流 )
FileInputStream( 文件输入流 )
PipedInputStream( 管道输入流 )
SequenceInputStream( 序列化输入流 )
StringBufferInputStream( 字符串缓冲输入流 )
ObjectInputStream( 对象输入流 )
FilterInputStream( 过滤器输入流 )
以下的类继承自 FilterInputStream( 过滤器输入流 ),同时实现了 DataInput
接口 。
LineNumberInputStream( 行号输入流 )
DataInputStream( 数据输入输入流 )
BufferedInputStream( 缓冲输入流 )
PushbackInputStream(推回输入流)
12.2.2输出流类 OutputStream
抽象类 OutputStream是表示输出数据流的抽象类,与抽象输入流对应,提供各种流对象的数据输出。下面介绍的输出流类,可以使读者了解输出流类的功能和使用方式。
OutputStream是个抽象类,提供了抽象 write方法,下面几个类是继承自 InputStream的子类,这些类都实现了 write()
方法:
ByteArrayOutputStream(字节数组输出流类)
FileOutputStream(文件输出流类)
ObjectOutputSteam(对象输出流类)
PipedOutputStream(管道输出流类)
FilterOutputStream(过滤器输出流类)
下面三个类继承自 FilterOutputStream类并实现了 DataOut
接口。
DataOutputStream(数据输出流类)
BufferedOutputStream(缓冲输出流类)
PrintStream(打印输出流类)
12.3 字节流
Java在设计其 I/O系统时,把输入输出的数据类型分为两类,一类是字符流,如上节介绍的
InputStream和 OutputStream类及其子类都是处理字符( 16bit)流。本节介绍字节( 8bit)流,字节流也分为读流数据类和写流数据类,即 Reader
类和 Writer类及其子类。
12.3.1 Writer类
Writer类是字符( Character)流输出类的父类,它是抽象类,所有继承自该类的子类都必须实现抽象方法 write,具体的实现类中 write方法的使用可以参考相应的 JavaDoc文档。这里为了区别 InputStream和 OutputStream使用了
Reader和 Writer,为了使读者习惯于使用 Reader和 Writer,
同时中文中没有合适的词汇表达相应的流的概念,所以不在具体翻译为中文,读者使用时只要知道 Reader类负责读流数据,而 Writer类负责向流中写数据。下面列出继承自
Writer类的子类。
BufferedWriter(带缓冲 Writer)
CharArrayWriter(字符数组 Writer)
FilterWriter(带过滤器 Writer)
PrintWriter(打印 Writer)
PipedWriter(管道 Writer)
StringWriter(字符串 Writer)
OutputStreamWriter(输出流 Writer)
12.3.2 Reader类
Reader类是读取字符( Character)流的父类,它是抽象类,
所有继承自该类的子类都必须实现抽象方法 read和 close,
具体的实现类中 read方法的使用可以参考相应的 JavaDoc文档。下面列出继承自 Reader类的子类。
BufferedReader(带缓冲 Reader)
CharArrayReader(字符数组 Reader)
FileReader(文件 Reader)
FilterReader(过滤器 Reader)
InputStreamReader(输入流 Reader)
LineNumberReader(带行号 Reader)
PipedReader(管道 Reader)
PushbackReader(推回 Reader)
StringReader(字符串 Reader)
12.4 File类
File类最初看起来仿佛是代表文件,其实这点
Java为该类起名确实有迷惑读者的地方,其实
File类可以表示特定文件名(带绝对路径),也可以是某个目录下多一组文件,该类提供了方法可以用来访问多个文件。 File类提供了丰富的方法来处理和文件或目录相关的操作。如创建和删除文件、创建和删除文件夹、通过和其他类配合使用实现文件的复制和移动等。本节将介绍 File
类提供的这些功能。
12.4.1 创建文件夹(目录)
File类提供了丰富的接口函数供用户调用。创建目录是文件操作中经常遇到的情形,目录提供了文件存放的位置,用户可以根据需要在磁盘空间上建立目录。建立目录的方法是调用 mkdir()方法,
代码为创建文件夹程序示例。
12.4.2 创建文件
在 Java的 File类中创建新文件只需要调用该类的一个方法 createNewFile(),但是在实际操作中需要注意一些事项,如判断文件是否存在,以及如何向新建文件中写入数据等。代码创建文件示例演示了上述讨论的问题。该程序首先创建一个用户指定名称和类型的数据,并向文件中写书数据。
12.4.3 复制文件
文件的复制设计到文件流的概念,在下节将更详细的介绍文件流操作,本节为了实现文件操作,
使用了 FileInputStream和 FileOutputStream两个流类。通过文件输入流读取源文件,通过文件输入流把读入缓冲区的字节数据写入新文件,如果该新文件已经存在则覆盖掉该文件如果不存在则新建一个文件,代码为复制文件程序示例。
12.4.4 删除文件
在 Java的 File类中删除文件只需要调用该类的一个方法 delete(),该方法可以删除指定的文件。
代码删除文件程序说明了该方法的具体使用方式。
在程序执行时,用户给出要删除的文件的目录和文件名或文件夹,就可以完成删除操作。
12.4.5 删除文件夹
在 Java的 File类中删除文件夹需要首先删除掉文件夹中的文件,再删除空文件夹,删除空文件夹的方法与删除文件的方法相同,所以关键是如何实现删除文件夹下的所有文件。上面已经知道如何删除一个文件,可以想象欲删除一个目录下的所有文件只要获得该文件的目录和文件名,使一个循环调用来依次删除文件夹中的文件即可。代码 12-9
就是依照这种思路实现了删除文件夹中的多个文件并删除文件夹。
该类提供了两个方法,一个方法是删除文件夹,另一个是删除文件夹下的文件。如果在删除文件夹时,既有目录又有文件,则删除文件,再继续删除文件夹,如果该文件夹下还是既有文件又有文件夹则继续上面的操作,直到把目录下的文件和子目录全部删除。该过程的流程图如图所示。
12.5 I/O流的典型运用
通过 12.3节和 12.4节的内容知道整个 I/O类库提供了两类流,一种是字符流,所有处理该字符
( Character)型数据的输入输出流类都继承自
InputStream和 OutStream。一种是字节 (Byte)流,
所有处理字节流数据的输入输出流类都继承自
Reader和 Writer类。
本节介绍 JavaI/O的几类典型应用,包括处理字节流和字符流数据。
12.5.1 文件流
文件流操作的目的是实现文件之间的数据传输,把数据从一个文件复制到另一个文件。文件的输入流可以是流类的对象如,FileReader,
FileInputStream。文件的输入流是一个流类的对象如,FileWriter,
FileOutputStream。 通过在文件上建立流,来实现文件间的数据传输。
代码文件流操作示例,展示了如何通过这些流类实现文件的复制。
12.5.2 读取内存数据
在 Java的输入输出流中 提供了读取内存数据的类,这些类包括
StringReader和 StringWriter,CharArrayReader和 CharArrayWriter、
ByteArrayInputStream和 ByteArrayOutputStream。在内存中读写数据通常是在已经存在的数组中创建 I/O流。本节以 StringReader和
StringWriter为例,这两个类用于从内存中的一个字符串中读写数据。首先看代码读内存数据示例。
12.5.3 链接文件
Java提供 SequenceInputStream类把多个输入流链接起来放在一个输入流中。多个输入流可以存入
Enumeration对象。将依次读取每个流对象内的数据,直到最后一个流对象的结尾。也可以直接链接两个输入流。这通过构造函数实现。代码就是链接文件的例子,首先创建两个输入流,再直接链接这两个输入流,打印到控制台输出,代码创建 RMI服务器程序示例。
12.5.4 管道流
两种管道流,一种是通过 PipedInputStream和
PipedOutputStream实现,一种是通过
PipedReader和 PipedWriter实现。
管道流是对应多线程的概念,实现线程间通信,
它建立在两个线程之上,它的实现原理是在管道的一端读入数据,而在管道的另一端读出输入。
实现管道流时,关键是在线程中建立管道。体现在下面两行关键语句中。
PipedWriter pipeOut = new PipedWriter();
PipedReader pipeIn = new
PipedReader(pipeOut);
12.5.5 随机访问文件
类 RandomAccessFile实现文件的随机访问,可以在文件的任意位置读取或写入数据。该类与
InputStream和 OutputStream不同。它把输入输出放入同一个类中,通过构造函数的参数确定是输入还是输出或输入输出操作可同时实现。该类的构造函数有两个参数,第一个参数是文件目录,
第二个参数指定相应的操作,,r” 读,,rw” 读写。
RandomAccessFile in = new
RandomAccessFile(“readme.txt”,”rw”);
RandomAccessFile in = new
RandomAccessFile(“readme.txt”,”r”);
12.5.6 从标准输入读取
按照标准 I/O模型,Java 提供了标准的输入输出方式,而
System.out是经过包装的流对象,可以将数据写出到标准输出,但是 System.in无法直接实现数据输入,因为
System.in是没有包装的流,所以在读取标准输入前必须对
System.in进行包装。
在代码中用 InputStreamReader来包装 System.in成 Reader,
再包装成 BufferedReader,先从标准输入读数据放入缓存,
再从缓存中读出数据赋予变量 s,最后从变量 s中读取数据在屏幕上输出。
12.5.7 I/O重定向
I/O重定向是指把标准输入定向到一个文件,把这个文件作为程序输入源,而把数据输出到一个指定的文件。因为 I/O操纵的是字节流,所以采用
InputStream和 OutputSteam流类族实现输入输出重定向。
12.5.8 过滤流
按照标准 I/O模型,Java 提供了标准的输入输出方式,而
System.out是经过包装的流对象,可以将数据写出到标准输出,
但是 System.in无法直接实现数据输入,因为 System.in是没有包装的流,所以在读取标准输入前必须对 System.in进行包装。
代码中用 InputStreamReader来包装 System.in成 Reader,再包装成 BufferedReader使用。
12.5.9 序列化对象
对象的序列化后的输入输出通过 ObjectInputStream和
ObjectOutputStream类实现。序列化的本质是把具有一定结构的 Java对象进行打包,而后通过特定的输入输出流来处理。本节将学习使用 Java提供的对象输入输出类来实现序列化对象的传输。
类 ObjectInputStream和 ObjectOutputStream不能单独使用,
必须附加在其他流之上,对其他输入输出流进行包装,因为对象的输入输出必须对应一个存储对象的文件,这里采用文件输入输出流对象作为对象输入输出流对象的参数来构造对象输入输出流对象,实现对象在文件之间的传输。
OjbectInputStream类提供了各种 read方法读取特定类型的数据,如 readObject(),readInt(),readBoolean()等方法读取流中的对象。 OjbectOutputStream类提供了各种
write()方法读取特定类型的数据,如 writeObject()、
writeInt(),writeBoolean()等方法向流中的写入数据。
12.6 习题
( 1)选择题
1,Java I/O流按处理的数据类型分为哪两类:
A,比特流和字符流 B,比特流和字节流
C,字符流和字节流 D,管道流和过滤流
2.实现多线程间通信使用的流是:
A,Object Stream B,Random Access Stream
C,File Stream D,Piped Stream
3.为了读取或修改一个文件的指定位置的数据,需要使用的流是:
A,Object Stream B,Random Access Stream
C,Filter Stream D,Piped Stream
4.有一个对象需要传输给另一个程序,此时考虑使用的流是:
A,Object Stream B,Random Access Stream
C,File Stream D,Piped Stream
5.常用的对读取文件数据的流是:
A,Object Stream B,Random Access Stream
C,File Stream D,Piped Stream
6.如果需要把多个输入流合并到一个输入流的是:
A,Object Stream B,Sequence object Stream
C,File Stream D,Piped Stream
12.6 习题
( 2)简答题
1.解释 I/O重定向的含义和设计到的输入输出类。
2.过滤流的功能以及设计到的类。
3.将多个输入流合并到一个输入流的类为
SequenceObjectStream,解释该类的两个构造函数。
4.参考 JavaDoc文档,查看 File类的各种方法,做到熟练使用文件的各种操作。
( 3)编程题
1.参考 12.5.1编写一个类,该类可以读取自己的,java文件,把该文件存储到一个,xls文件中,并在控制台打印文件内容。
2.改写 12.5.3节的示例程序,调用 SequenceInputStream
的另一个构造函数实现多个(大于 2个)文件的链接任务,
该构造函数形式为 SequenceInputStream(Enumeration
enu)。