第 11章 输入输出流及文件与外部设备和其他计算机进行交流的输入输出操作,尤其是对磁盘的文件操作,是计算机程序重要而必备的功能,任何计算机语言都必须对输入输出提供支持。 Java语言也不例外,它的输入输出类库中包含了丰富的系统工具 ─ 已定义好的用于不同情况的输入输出类;利用它们,Java程序可以很方便地实现多种输入输出操作和复杂的文件与目录管理。
本章要点
Java输入输出类库
字符的输入与输出
数据输入输出流
Java程序的文件与目录理第五讲 输入 /输出处理
I/O流概述
字节流
字节流的基类
文件流
过滤流
标准流
字符流
文件操作 /随机访问文件
对象流
管道流
内存读写流
顺序输入流
11.1 Java输入输出类库 — 流的概念大部分程序都需要输入 /输出处理,比如从键盘读取数据、向屏幕中输出数据、从文件中读或者向文件中写数据、在一个网络连接上进行读写操作等。在 Java中,把这些不同类型的输入、输出源抽象为流( Stream),而其中输入或输出的数据则称为数据流( Data Stream),
用统一的接口来表示,从而使程序设计简单明了。
文件程序终端文件程序网络端点 数据流起点终点网络端点文件,字符串存储区在 JDK1.1之前,java.io包中的流只有普通的 字节流 (以
byte为基本处理单位的流),这种流对于以 16位的
Unicode码表示的字符流处理很不方便。
从 JDK1.1开始,java.io包中加入了专门用于 字符流 处理的类(以 Reader和 Writer为基础派生的一系列类)。
另外,为了使对象的状态能够方便地永久保存下来,
JDK1.1以后的 java.io包中提供了以字节流为基础的用于对象的永久化保存状态的机制 ——对象流 (通过实现
ObjectInput和 ObjectOutput接口)。
11.1 Java输入输出类库 — 流的概念流一般分为输入流( Input Stream)和输出流( Output
Stream)两类,但这种划分并不是绝对的。比如一个文件,
当向其中写数据时,它就是一个输出流;当从其中读取数据时,它就是一个输入流。当然,键盘只是一个输入流,
而屏幕则只是一个输出流。
在 Java开发环境中,主要是由包 java.io中提供的一系列的类和接口来实现输入 /输出处理。标准输入 /输出处理则是由包 java.lang中提供的类来处理的,但这些类又都是从包 java.io中的类继承而来。
输入流:数据提供者,可从中读取数据出来输出流:数据接收者,可往其中写数据
11.1 Java输入输出类库 — 基本输入输出流类
InputStream类
InputStream中包含一套所有输入流都需要的方法,可以完成最基本的自输入流读入数据的功能。
当 Java程序需要从外设读入数据时,它应该创建一个适当类型的输入流类的对象来完成与该外设,如键盘、磁盘文件或网络套接字等的连接。然后再调用执行这个新创建的流类对象的特定方法,实现对相应外设的输入操作。需要说明的是,由于 InputStream是不能被实例化的抽象类,所以在实际程序中创建的输入流一般都是 InputStream的某个子类的对象,由它来实现与外设数据源的连接。
11.1 Java输入输出类库 — 基本输入输出流类
InputStream类常用方法
11.1 Java输入输出类库 — 基本输入输出流类类型 方法 简要说明读入数据方法 int read() 从流中读取一个字节的数据
int read(byte b[]) 将流中某些字节数据读入到一个字节数组中
int read(byte b[],int off,int len) 从流中读取指定长度的数据放入到一个字节数组中定位输入位置指针方法 long skip(long n) 在流中跳过 n个字节
void mark() 在流的当前位置设置一个标记
void reset() 返回上一个标记关闭流方法 void close() 当输入操作完毕时,关闭流
OutputStream类
OutputStream中包含一套所有输出流都要使用的方法。与读入操作一样,当 Java程序需要向某外设,如屏幕、磁盘文件或另一计算机输出数据时,应该创建一个新的输出流对象来完成与该外设的连接,然后利用 OutputStream提供的 write()方法将数据顺序写入到这个外设上。
11.1 Java输入输出类库 — 基本输入输出流类
OutputStream类常用方法
11.1 Java输入输出类库 — 基本输入输出流类类型方法 简要说明写入数据方法
Void write(int b) 将参数的低位字节写入到输出流
Void write(byte b[]) 将字节数组 b[]中的全部字节顺序写入到输出流
Void flush() 数据暂时放在缓冲区中,等积累到一定数量,统一一次向外设写入。
关闭流方法
void close() 当输出操作完毕时,关闭流
基本输入输出流是定义基本输入输出操作的抽象类,在 Java程序中真正使用的是它们的子类。它们对应不同的数据源和输入输出任务,以及不同的输入输出流。较常用的输入输出流如下表
11.1 Java输入输出类库 — 其他输入输出流类输入输出流类型输入输出流名 简要说明过滤输入输出流
FilterInputStream 主要特点是输入输出数据的同时,能对所传输的数据做指定类型或格式的转换,即可实现对二进制字节数据的理解和编码转换FilterOutputStream
文件
FileInputStream 主要负责完成对磁盘文件的顺序读写操作
FileOutputStream
管道输入输出流
PipedInputStream 主要负责实现程序内部的线程间的通信或不同程序间的通信
PipedOutputStream
字节数组流
ByteArrayInputStream 主要实现与内存缓冲区的同步读写
ByteArrayOutputStream
顺序输入流
SequenceInputStream 主要负责把两个其他的输入流首尾相接,合并成一个完整的输入流当 Java程序需要与外设等外界数据源做输入、输出的数据交换时,它需要首先创建一个输入或输出类的对象来完成对这个数据源的连接。
如当 Java程序需要读写文件时,它需要先创建文件输入或文件输出流类的对象。除文件外,程序也经常使用字符界面的标准输入、输出设备进行读写操作。
计算机系统都有默认的标准输入设备和标准输出设备。对一般的系统,
标准输入通常是键盘,标准输出通常是显示器。 Java程序使用字符界面与系统标准输入、输出间进行数据通信,即从键盘输入数据,或向显示器输出数据,是十分常见的操作。为此,Java系统事先定义好两个流对象,分别与系统的标准输入和标准输出相联系,它们是
System.in和 System.out。
System是 Java中一个功能很强大的类,利用它可以获得很多 Java运行时的系统信息。 System类的所有属性和方法都是静态的,即调用时需要以类名 System为前缀。 System.in和 System.out就是 System类的两个静态属性,分别对应了系统的标准输入和标准输出。
11.1 Java输入输出类库 — 标准输入输出
标准输入
Java的标准输入 System.in是 InputStream类的对象,当程序中需要从键盘输入数据的时候,只需调用 System.in的 read()方法即可。在使用
System.in.read()方法读取数据时,需要注意以下 4点:
11.1 Java输入输出类库 — 标准输入输出
( 1) System.in.read()语句必须包含在 try块中,且 try块后面应该有一个可接收 IOException例外的 catch块。
( 2)执行 System.in.read()方法将从键盘缓冲区读取一个字节的数据,然而返回的却是 16位的整型量,需要注意的是只有这个整型量的低位字节是真正输入的数据,其高位字节是全零。
( 3) System.in.read()只能从键盘读取二进制的数据,而不能是其他类型的数据。
( 4)当键盘缓冲区中没有未被读取的数据时,执行 System.in.read()将导致系统转入阻塞状态。在阻塞状态下,当前流程将停留在上述语句位置且整个程序被挂起,等待用户输入一个键盘数据后,才能继续运行下去;所以程序中有时利用 System.in.read()语句来达到暂时保留屏幕的目的。
标准输出
Java的标准输出 System.out是打印输出流 PrintStream类的对象。
PrintStream是过滤输出流类 FilterOutputStream的一个子类,其中定义了向屏幕输送不同类型数据的方法 print()和 println()。
println()方法有多种重载形式。它的作用是向屏幕输出其参数指定的变量或对象,然后再换行,使光标停留显示在屏幕下一行第一个字符的位置。如果 println()参数为空,则将输出一个空行。
11.1 Java输入输出类库 — 标准输入输出
标准输出
println()方法可输出多种不同类型的变量或对象,包括 boolean,
double,float,int,long类型的变量及 Object类的对象。由于
Java中规定子类对象作为实际参数可以与父类对象类型的变量及
Object类的对象。由于 Java中规定子类对象作为实际参数可以与父类对象的形式参数匹配,而 Object类又是所有 Java类的父类,
所以 println()方法实际可以通过重载实现对所有类对象的屏幕输出。
print()方法的重载情况与 println()方法完全一样,也可以实现屏幕上不同类型的变量和对象的操作。不同的是,print()方法输出对象后并不附带一个回车,下一次输出时,显示在同一行。
11.1 Java输入输出类库 — 标准输入输出
11.2 字符的输入与输出通过上一节的学习,我们知道按处理数据的类型,流可以分为字符流与字节流,它们处理的信息的基本单位分别是字符和字节。当我们从文件中读取一段文本,该文件可能是 ASCII格式的字符,也可能是 Unicode格式的字符。如果是 Unicode格式的字符,那以字节为单位处理就比较麻烦。
为了解决这个问题,Java提供了一套流过滤器,用于处理不同字符编码之间的差别。它们是从 Reader和 Writer类派生的。
字符输入用的流为 Reader,字符输出用的流为 Writer。
在 JDK1.1之前,java.io包中的流只有普通的字节流(以
byte为基本处理单位的流),这种流对于以 16位的 Unicode
码表示的字符流处理很不方便。从 JDK1.1开始,java.io包中加入了专门用于字符流处理的类,它们是以 Reader和
Writer为基础派生的一系列类。
同类 InputStream和 OutputStream一样,Reader和
Writer也是抽象类,只提供了一系列用于字符流处理的接口。
它们的方法与类 InputStream和 OutputStream类似,只不过其中的参数换成字符或字符数组。
字节流中类 DataInputStream的 readLine方法,可以以字节形式读入,以 Unicode形式输出( String readLine())。
byte Unicode 16bit8 bit + 00000000
11.2 字符的输入与输出
void close()
void mark(int readAheadLimit)
boolean markSupported(),
int read()
int read(char[] cbuf)
int read(char[] cbuf,int off,int len)
boolean ready()
void reset()
long skip(long n)
11.2 字符的输入与输出 —— 基类,Reader
11.2 字符的输入与输出 —— 基类,Writer
void close()
void flush()
void write(char[] cbuf)
void write(char[] cbuf,int off,int len)
void write(int c)
void write(String str)
void write(String str,int off,int len)
11.2 字符的输入与输出 —— 字符流
(InputStreamReader和 OutputStreamWriter)
InputStreamReader和 OutputStreamWriter是 java.io包中用于处理字符流的最基本的类,用来在字节流和字符流之间作为中介,从字节输入流读入字节,并按编码规范转换为字符;往字节输出流写字符时先将字符按编码规范转换为字节 。使用这两者进行字符处理时,在构造方法中应指定一定的平台规范,
以便把以字节方式表示的流转换为特定平台上的字符表示。
InputStreamReader(InputStream in); //缺省规范
InputStreamReader(InputStream in,String enc); //指定规范 enc
OutputStreamWriter(OutputStream out); //缺省规范
OutputStreamWriter(OutputStream out,String enc); //指定规范 enc
如果读取的字符流不是来自本地时(比如网上某处与本地编码方式不同的机器),那么在构造字符输入流时就不能简单地使用缺省编码规范,而应该指定一种统一的编码规范,ISO 8859_1”,这是一种映射到 ASCII码的编码方式,
能够在不同平台之间正确转换字符。
InputStreamReader ir = new InputStreamReader( is,“8859_1”);
11.2 字符的输入与输出 —— 字符流
(InputStreamReader和 OutputStreamWriter)
11.2 字符的输入与输出 —— 缓存流
(BufferedReader和 BufferedWriter)
同样的,为了提高字符流处理的效率,java.io中也提供了缓冲流 BufferedReader和 BufferedWriter。其构造方法与
BufferedInputStream和 BufferedOutputStream相类似。另外,除了 read()和 write()方法外,它还提供了整行字符处理方法:
public String readLine(),BufferedReader的方法,从输入流中读取一行字符,行结束标志为 ‘ \n’,‘ \r’或两者一起。
public void newLine(),BufferedWriter的方法,向输出流中写入一个行结束标志,它不是简单的换行符 ‘ \n’或
‘ \r’,而是系统定义的行隔离标志( line separator)。
11.2 字符的输入与输出 —— 其它字符流
CharArrayReader
CharArrayWriter 对字符数组进行处理
LineNumberReader:行处理字符流
PrintWriter,打印字符流
FileReader
FileWriter 对文本文件进行处理
StringReader
StringWriter 对字符串进行处理
FilterReader
FilterWriter 过滤字符流
PipedReader
PipedWriter 管道字符流
11.3 数据输入输出流数据输入输出流 DataInputStream和
DataOutStream分别是过滤输入输出流
FileterInputStream和 FileterOutStream的子类。过滤输入输出流的最主要作用就是在数据源和程序之间一个过滤处理步骤,对原始数据做特定的加工、处理和变换操作。数据输入输出流 DataInputStream和
DataOutStream由于分别实现了 DataInput和
DataOutput两个接口中定义的独立于具体机器的带格式的读写操作,从而实现了对不同类型数据的读写。
11.3 数据输入输出流在 DataInput接口中,定义了一些常用的方法用于基本数据类型的数据的输入,如下表:
方法 简要说明
boolean readBoolean() 读取一个布尔值
byte readByte() 读取一个字节
double readDouble() 读取一个双精度浮点数
float readFloat() 读取一个浮点数
int readInt() 读取一个整数
long readLong() 读取一个长整数
short readShort() 读取一个短整数
String readUTF() 读取一个 UTF格式的字符串
11.3 数据输入输出流与 DataInput相对应,DataOutput接口也定义了一些常用的方法,如下表:
方法 简要说明
boolean writeBoolean() 读取一个布尔值
byte writeByte() 读取一个字节
double writeDouble() 读取一个双精度浮点数
float writeFloat() 读取一个浮点数
int writeInt() 读取一个整数
long writeLong() 读取一个长整数
short writeShort() 读取一个短整数
String writeUTF() 读取一个 UTF格式的字符串
11.4 Java程序的文件与目录任何计算机程序运行时,它的指令和数据都保存在系统的内存中。
由于每次计算机关机时保存在内存中的所有信息都会丢失,所以程序要想永久性保存运算处理所得的结果,就必须把这些结果保存在磁盘文件中。文件是数据赖以保存的永久性机制,文件操作是计算机程序必备的功能。
目录是管理文件的特殊机制,同类文件保存在同一个目录下可以简化文件管理,提高工作效率。 Java语言不但支持文件管理,还支持其他语言,如 C语言所不支持的目录管理,它们都是有专门的类 File来实现的。 File类也在 java.io包中,但它不是 InputStream或者 OutputStream的子类,因为它不负责数据的输入输出,而专门用来管理磁盘文件和目录。
每个 File类的对象表示一个磁盘文件或目录,其对象属性中包含了文件或目录的相关信息,如名称、长度、所含文件个数等,调用它的方法则可以完成对文件或目录的常用管理操作,如创建、删除等。
File:以文件路径名的形式代表一个文件
FileDescriptor:代表一个 打开文件 的文件描述
FileFilter & FilenameFilter:用于列出满足条件的文件
File.list(FilenameFilter fnf)
File.listFiles(FileFilter ff)
FileDialog.setFilenameFilter(FilenameFilter fnf)
FileInputStream & FileReader:顺序读文件
FileOutputStream & FileWriter:顺序写文件
RandomAccessFile:提供对文件的随机访问支持。
11.4 Java程序的文件与目录
11.4 Java程序的文件与目录 —— 文件操作,File
File(String pathname)
File f=new File(“c:\data\temp.dat”);
File f=new File(“data\ temp.dat”);
File f=new File(“temp.dat”);
File(String parent,String child)
File f=new File(“c:\data”,“temp.dat”);
File f=new File(“data,,,temp.dat”);
File(File parent,String child)
File f=new File(new File(“c:\data”),“temp.dat”);
File f=new File(new File(“data,),,temp.dat”);
boolean canRead()
boolean canWrite()
boolean setReadOnly()
boolean exists()
boolean isDirectory()
boolean isFile()
boolean isHidden()
long lastModified()
boolean setLastModified(long time)
long length()
11.4 Java程序的文件与目录 —— 文件操作,File
String[] list()
String[] list(FilenameFilter filter)
File[] listFiles()
File[] listFiles(FileFilter filter)
File[] listFiles(FilenameFilter filter)
static File[] listRoots()
boolean mkdir()
boolean mkdirs()
(粉色的方法在 JDK1.2之后才支持)
11.4 Java程序的文件与目录 —— 文件操作,File
boolean createNewFile()
static File createTempFile(String prefix,String suffix)
static File createTempFile(String prefix,String suffix,
File directory)
boolean delete()
void deleteOnExit()
boolean renameTo(File dest)
11.4 Java程序的文件与目录 —— 文件操作,File
String getName()
File getParentFile()
String getParent()
String getPath()
boolean isAbsolute()
File getAbsoluteFile()
String getAbsolutePath()
File getCanonicalFile()
String getCanonicalPath()
11.4 Java程序的文件与目录 —— 文件操作,File
FileInputStream & FileOutputStream & RandomAccessFile
FileDescriptor getFD()
通过 FileDescriptor构造输入输出流
FileInputStream(FileDescriptor fdObj)
FileOutputStream(FileDescriptor fdObj)
FileReader(FileDescriptor fd)
FileWriter(FileDescriptor fd)
例如:
FileInputStream fin = new FileInputStream(“file.txt”);
FileReader fr = new FileReader(fin.getFD());
11.4 Java程序的文件与目录 —— 文件操作,FileDescriptor
对于 FileInputStream/FileOutputStream,FileReader/FileWriter
来说,它们的实例都是顺序访问流,即只能进行顺序读 /写。
而类 RandomAccessFile则允许对文件内容同时完成读和写操作,
它直接继承 object,并且同时实现了接口 DataInput和 DataOutput,
提供了支持随机文件操作的方法:
readXXX()或 writeXXX():
如 ReadInt(),ReadLine(),WriteChar(),WriteDouble()等。
int skipBytes(int n):将指针乡下移动若干字节
length():返回文件长度
long getFilePointer():返回指针当前位置
void seek(long pos):将指针调到所需位置 pos
file
11.4 Java程序的文件与目录
—— 随机访问文件( RandomAccessFile )
在生成一个随机文件对象时,除了要指明文件对象和文件名之外,还需要指明访问文件的模式。
RandomAccessFile(File file,String mode)
RandomAccessFile(String name,String mode)
mode 的取值:
,r”只读,任何写操作都将抛出 IOException。
,rw”读写,文件不存在时会创建该文件,文件存在时,原文件内容不变,通过写操作改变文件内容。
,rws”同步读写,等同于读写,但是任何写操作的内容都被直接写入物理文件,包括文件内容和文件属性。
,rwd”数据同步读写,等同于读写,但任何内容写操作都直接写到物理文件,但对文件属性内容的修改不是这样。
11.4 Java程序的文件与目录
—— 随机访问文件( RandomAccessFile )
File f = new File(“file.txt”);
new RandomAccessFile(f,“r”);
new RandomAccessFile(f,“rw”);
new RandomAccessFile(“file1.txt”,“r”);
new RandomAccessFile(“file2.txt”,“rw”);
11.4 Java程序的文件与目录
—— 随机访问文件( RandomAccessFile )
小结
在 Java中有数据传输的地方都用到 I/O流 (通常是文件、网络、
内存和标准输入输出等 )。
InputStream 和 OutputStream是所有字节流的祖先 (只有
RandomAccessFile类是一个例外 ),read和 write是它们最基本的方法,读写单位是字节。
Reader 和 Writer是所有字符流的祖先,read和 write是它们最基本的方法,读写单位是字符。
在众多的流对象中,并不是每一种都单独使用,其中过滤流的子类在数据送出去之前做必要的处理。
文件 文件输入流 缓冲输入流 行号输入流 数据输入流 目的小结
File,File(Input/Output)Stream,RandomAccessFile是处理本地文件 的类。
Data(Input/Output)Stream是一个过滤流的子类,借此可以读写各种基本数据,在文件和网络中经常使用。如,
readByte,writeBoolean等。
Buffered(Input/Output)Stream的作用是在数据送到目的之前先缓存,达到一定数量时再送到目的,以减少阻塞次数。
Piped(Input/Output)Stream适合与一个处理的输出作为另一个处理的输入的情况。