流是对 I/O 操作的形象描述,水从一个地方转移到另一个地方就形成了水流,而信息从一处转移到另一处就叫做 I/O 流。
在 Java 中,文件的输入和输出是通过流(Stream)来实现的,流的概念源于 UNIX 中管道(pipe)的概念。在 UNIX 系统中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。
一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是 Internet 上的某个 URL。对于流而言,不用关心数据是如何传输的,只需要从源端输入数据(读),向目的端输出数据(写)。
输入流示意图如下所示:
输出流示意图如下所示:
输入流的抽象表示形式是接口 InputStream
;输出流的抽象表示形式是接口 OutputStream
。
JDK 中 InputStream
和 OutputStream
的实现就抽象了各种方式向内存读取信息和向外部输出信息的过程。之前常用的 System.out.println();
就是一个典型的输出流,目的是将内存中的数据输出到控制台。而 new Scanner(System.in);
就是一个典型的输入流,目的是将控制台接收的信息输入到内存中。 System.in
和 System.out
两个变量实际就是 InputStream
和 OutputStream
的实例对象。以 InputStream
对象为例,如下是定义 System
的源码。
public final class System {
...
public final static InputStream in = null;
...
}
字节流和字符流
按照处理数据的单位,流可以分为字节流和字符流。字节流的处理单位是字节,通常用来处理二进制文件,如音乐、图片文件等,并且由于字节是任何数据都支持的数据类型,因此字节流实际可以处理任意类型的数据。而字符流的处理单位是字符,因为 Java 采用 Unicode 编码,Java 字符流处理的即 Unicode 字符,所以在操作文字、国际化等方面,字符流具有优势。
字符流和字节流只是适应于不同的文件类型,并没有效率高低之分。
字节流的输入输出类
输入字节流类继承自抽象类 InputStream
,输出字节流继承自抽象类 OutputStream
,这两个抽象类拥有的方法可以通过查阅 Java API 获得。
JDK 提供了不少字节流的实现类,下面列举了六个输入字节流类,输出字节流类和输入字节流类存在对应关系。
FileInputStream
:把一个文件作为输入源,从本地文件系统中读取数据字节,实现对文件的读取操作。ByteArrayInputStream
:把内存中的一个缓冲区作为输入源,从内存数组中读取数据字节。ObjectInputStream
:对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化,用于恢复那些以前序列化的对象,注意这个对象所属的类必须实现 Serializable 接口(网络编程会用到)。PipedInputStream
:实现了管道的概念,从线程管道中读取数据字节。主要在线程中使用,用于两个线程间的通信。SequenceInputStream
:其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直至到达文件末尾,接着从第二个输入流读取,依次类推。System.in
:从用户控制台读取数据字节,在System
类中,in 是InputStream
类型的静态成员变量。
字符流的输入输出类
读取字符流类继承自抽象类 Reader
,写入字符流继承自抽象类 Writer
。并且 Reader
的子类必须实现的方法只有 read(char[], int, int)
和 close()
。但是,多数子类还重写了 Writer
或 Reader
类定义的一些其他方法,以提供更高的效率或完成其他功能。
Reader
和 Writer
要解决的一个主要问题是国际化。原先的 I/O 类库只支持 8 位的字节流,因此不能很好地处理 16 位的 Unicode 字符。Unicode 是国际化的字符集,这样在增加了 Reader
和 Writer
之后,就可以自动在本地字符集和 Unicode 国际化字符集之间进行转换,程序员在应对国际化时不需要做过多额外的处理。
JDK 提供了一些字符流实现类,下面列举了部分输入字符流类,同样,输出字符流类和输入字符流类存在对应关系。
FileReader
:与FileInputStream
对应,从文件系统中读取字符序列。CharArrayReader
:与ByteArrayInputStream
对应,从字符数组中读取数据。PipedReader
:与PipedInputStream
对应,从线程管道中读取字符序列。StringReader
:从字符串中读取字符序列。
Writer
输出字符流的可用方法。注意,这些方法操作的数据是 char
相关类型,不是 byte
类型。
Writer append(char c)
:将指定字符 c 追加到此 Writer,此处是追加,不是覆盖。Writer append(CharSequence csq)
:将指定字符序列 csq 添加到此 Writer。Writer append(CharSequence csq, int start, int end)
:将指定字符序列 csq 的子序列,追加到此 Writer。void write(char[] cbuf)
:写入字符数组 cbuf。void write (char[] cbuf, int off, int len)
:写入字符数组 cbuf 的某一部分。void write(int c)
:写入单个字符 c。void write(String str)
:写入字符串 str。void write(String str, int off, int len)
:写入字符串 str 的某一部分。void close()
:关闭当前流。