I/O流概述
在 Java 中,把不同类型的输入、输出源抽象为流(Stream),而其中输入或输出的数据则称为数据流(Data Stream),用统一的接口表示,从而使程序设计简单明了。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
字节流基类
字节流主要操作byte类型数据,以byte数组为准,java 中每一种字节流的基本功能依赖于基本类 InputStream 和 Outputstream,他们是抽象类,不能直接使用。字节流能处理所有类型的数据(如图片、avi等)。
InputStream
此抽象类是表示字节输入流的所有类的超类。InputStream 是从装置来源地读取数据的抽象表示,例如 System 中的标准输入流 in 对象就是一个InputStream 类型的实例。
只有默认的无参构造器,类中定义的其它方法如下:
方法 描述 int available() 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。 void close() 关闭此输入流并释放与该流关联的所有系统资源。 void mark(int readlimit) 在此输入流中标记当前的位置。 boolean markSupported() 测试此输入流是否支持 mark 和 reset 方法。 abstract int read() 从输入流中读取数据的下一个字节。 int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte[off] 至 byte[off + len -1]。 void reset() 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。 long skip(long n) 跳过和丢弃此输入流中数据的 n 个字节。 对于三个read()方法,若返回-1,表明流结束,否则,返回实际读取的字符数。
OutputStream
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。例如 System 中的标准输出流对象 out 其类型是java.io.PrintStream,这个类是 OutputStream 的子类。
只有默认的无参构造器,类中定义的其它方法如下:
方法 描述 void close() 关闭此输出流并释放与此流有关的所有系统资源。 void flush() 刷新此输出流并强制写出所有缓冲的输出字节。 void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。 void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 abstract void write(int b) 将指定的字节写入此输出流。 一般来说,很少直接实现 InputStream 或 OutputStream 上的方法,因为这些方法比较低级,通常会实现它们的子类。
文件流
File 类
首先来了解一下File类。Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。
构造方法:
//根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。 File(File parent, String child); //通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 File(String pathname); // 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。 File(String parent, String child); // 通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。 File(URI uri);
经常使用File类的下列方法获取文件本身的一些信息:
方法 描述 public String getName() 获取文件的名字。 public boolean canRead() 判断文件是否是可读的。 public boolean canWrite() 判断文件是否是可被写入的。 public boolean exits() 判断文件是否存在。 public long length() 获取文件的长度(单位是字节)。 public String getAbsolutePath() 获取文件的绝对路径。 public String getParent() 获取文件的父目录。 public boolean isFile() 判断文件是否是一个正常文件,而不是目录。 public boolean isDirectory() 判断文件是否是有个目录。 public boolean isHidden() 判断文件是否是隐藏文件。 public long lastModified() 获取文件最后修改的时间(返回距1970.01.01的毫秒数)。 关于目录、文件创建与删除等内容不做展开,可以去JDK 1.6 在线中文手册中查看File类的方法。
java.io 包中所提供的文件操作类包括:
- 用于读写本地文件系统中的文件:FileInputStream 和 FileOutputStream
- 描述本地文件系统中的文件或目录:File、FileDescriptor 和 FilenameFilter
- 提供对本地文件系统中文件的随机访问支持:RandomAccessFile
FileInputStream
FileInputStream 类用于打开一个输入文件,若要打开的文件不存在,则会产生异常 FileNotFoundException,这是一个非运行时异常,必须捕获或声明抛弃。
构造方法:
//打开一个以 f 描述的文件作为输入 FileInputStream(File f); //打开一个文件路径名为 name 的文件作为输入 FileInputStream(String name);
从输入流中读取字节,可以用上面介绍的 InputStream 类中三个 read 方法。
FileOutputStream
FileOutputStream 类用来打开一个输出文件,若要打开的文件不存在,则会创建一个新的文件,否则原文件的内容会被新写入的内容所覆盖。
构造方法:
//创建一个以 f 描述的文件作为输出 //如果文件存在,则其内容被清空 FileOutputStream(File f); //创建一个文件路径名为 name 的文件作为输出 //文件如果已经存在,则其内容被清空 FileOutputStream(String name); //创建一个文件路径名为 name 的文件作为输出 //文件如果已经存在,则在该输出上输出的内容被接到原有内容之后 FileOutputStream(String name, boolean append);
使用上面 OutputStream 类中介绍的 write() 方法把字节写入到输出流到达目的地,只要不关闭流,就可以一直调用顺序写入。
注:虽然 Java 在程序结束时自动关闭所有打开的流,但是当我们使用完流之后,显式地关闭任何打开的流仍是一个良好的习惯(调用 close() 方法)。原因是一个打开的流会占用一定的系统资源,关闭输出流还可以把该流缓冲区的内容冲洗掉,即写到它的目的地。
缓冲流
类 BufferedInputStream 和 BufferedOutputStream 实现了带缓冲的过滤流,它提供了缓冲机制,把任意的 I/O 流“捆绑”到缓冲流上,可以提高 I/O 流的读取效率。在初始化时,除了要指定所连接的 I/O 流之外,还可以指定缓冲区的大小。
BufferedInputStream
BufferedInputStream 的数据成员 buf 是一个位数组,默认为2048字节。当读取数据来源时例如文件,BufferedInputStream 会尽量将 buf 填满。当使用 read ()方法时,实际上是先读取 buf 中的数据,而不是直接对数据来源作读取。当 buf 中的数据不足时,BufferedInputStream 才会再实现给定的 InputStream 对象的 read() 方法,从指定的装置中提取数据。不过也可以使用 flush() 方法人为地将尚未填满的缓冲区中的数据送出。
构造方法:
BufferedInputStream(InputStream in); BufferedInputStream(InputStream in, int size);
BufferedOutputStream
BufferedOutputStream 的数据成员 buf 是一个位数组,默认为512字节。当使用 write() 方法写入数据时,实际上会先将数据写至 buf 中,当 buf 已满时才会实现给定的 OutputStream 对象的 write() 方法,将 buf 数据写至目的地,而不是每次都对目的地作写入的动作。
构造方法:
BufferedOutputStream(OutputStream out); BufferedOutputStream(OutputStream out, int size);
数据流
接口 DataInput 和 DataOutput,设计了一种较为高级的数据输入输出方式:除了可处理字节和字节数组外,还可以处理 int、float、boolean等基本数据类型,这些数据在文件中的表示方式和它们在内存中的一样,无须转换,如 read(), readInt(), readByte()...; write(), writeChar(), writeBoolean()...此外,还可以用 readLine()方法读取一行信息。
数据流类 DateInputStream 和 DataOutputStream 的处理对象除了是字节或字节数组外,还可以实现对文件的不同数据类型的读写:
- 分别实现了 DataInput 和 DataOutput 接口
- 在提供字节流的读写手段同时,以统一的形式向输入流中写入 boolean,int,long,double 等基本数据类型,并可以再次把基本数据类型的值读取回来。
- 提供了字符串读写的手段
相关知识
标准流
语言包 java.lang 中的 System 类管理标准输入/输出流和错误流。
- System.in从 InputStream 中继承而来,用于从标准输入设备中获取输入数据(通常是键盘)
- System.out从 PrintStream 中继承而来,把输入送到缺省的显示设备(通常是显示器)
- System.err也是从 PrintStream 中继承而来,把错误信息送到缺省的显示设备(通常是显示器)
每当 main 方法被执行时,就会自动生产上述三个对象。
内存读写流
为了支持在内存上的 I/O,java.io 中提供了类:ByteArrayInputStream、ByteArrayOutputStream 和 StringBufferInputStream
- ByteArrayInputStream 可以从指定的字节数组中读取数据
- ByteArrayOutputStream 中提供了缓冲区可以存放数据(缓冲区大小可以在构造方法中设定,缺省为32),可以用 write() 方法向其中写入数据,然后用 toByteArray() 方法将缓冲区中的有效字节写到字节数组中去。size() 方法可以知道写入的字节数;reset() 可以丢弃所有内容。
- StringBufferInputStream 与 ByteArrayInputStream 相类似,不同点在于它是从字符缓冲区 StringBuffer 中读取16位的 Unicode 数据,而不是8位的字节数据(已被 StringReader 取代)
顺序输入流
java.io 中提供了类 SequenceInputStream,使应用程序可以将几个输入流顺序连接起来。顺序输入流提供了将多个不同的输入流统一为一个输入流的功能,这使得程序可能变得更加简洁。
参考资料
- 实验楼:JDK 核心 API
- Java2实用教程 (第三版)_ 耿祥义,张跃平