概述
Java I/O分为两大类: 字节与数字型 I/O 和 字符与文本型 I/O。而Java中提供的所有I/O操作类,都是基于流(Stream)的概念。
流(Stream)即非确定长度的有序字节序列。
无论是字节流还是字符流,二者都是基于字节数据,只不过数据的宽度不同。
Java中默认 字节流按照1byte(8bit)单位数据量读取,字符流按照2byte(16bit,即Java默认的内存字符存储格式 Unicode(UTF-16))。
按照上述两种概念,Java在具体实现当中,提供了如下两种方式:
- 字节输入流(InputStream)与字节输出流(OutputStream)
- 字符输入流(Reader)与字符输出流(Writer)
用户可以通过上述两种流操作,进行本地数据、网络数据的读写,也可以通过这些流操作达成程序间数据交互的目的。Java还在基本读写功能的基础上,提供了压缩、加密、数据过滤等功能类。
本文的重点在于介绍基础I/O读写操作类及相互之间的关系。
I/O流类介绍
Java 8中java.io包下所有的流关系图如下:
关于每个类的概要描述,可以参考 java.io包类摘要,或者到官网查询相关的文档资料,也可以下载相关的源码进行阅读。
节点流和处理流
Java当中按照流的处理位置,还可以分为节点流和处理流。
- 节点流 可以从或向一个特定的地方(节点)读写数据。
- 处理流 是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
节点流类
节点流类在java.io中所占比例较少,主要用于最基本的数据处理:
abstract InputStream
- ByteArrayInputStream 字节数组输入流,区别于CharArrayReader
- FileInputStream 文件字节输入流,区别于FileReader
- PipedInputStream 管道字节输入流,区别于PipedReader
- StringBufferInputStrem 已过时,使用StringReader替代
abstract OutputStream
- ByteArrayOutputStream 字节数组输出流,区别于CharArrayWriter
- FileOutputStream 文件字节输出流,区别于FileWriter
- PipedOutputStream 管道字节输出流,区别于PipedWriter
abstract Reader
- CharArrayReader 字符数组输入流,区别于ByteArrayReader
- FileReader 文件字符输入流,区别于FileInputStream
abstract Writer
- CharArrayWriter 字符数组输出流,区别于ByteArrayWriter
- FileWriter 文件字符输出流,区别于FileOutputStream
- PipedWriter 管道字符输出流,区别于PipedOutputStream
- StringWriter 字符串输出流,不会抛出IOException
处理流类
从java.io包中类的角度讲,继承了 FilterInputStream, FilterOutputStream, FilterReader, FilterWriter的类均为是处理流。
这四个类及其子类,如基类命名的Filter一样,是作为”过滤”的中间介质存在,可以对已存在的流进行过滤处理,包含加密、压缩、转译、缓冲等特殊操作。譬如使用BufferedInputStream过滤FileInputStream,即可拥有缓冲功能,从而提高文件读取性能。在java.io以外的包,譬如java.security中有关于信息加密解密的流,亦继承于FilterInputStream等过滤类。
值得注意的是,上述四个Filter类中,字符流的两个Filter基类(FilterReader/FilterWriter)均为抽象(abstract)方法,而字节流的是具体方法。
继承Filter类的处理流类有:
FilterInputStream
- BufferedInputStream 缓冲字节输入流,System.in 即为该类的一个实例
- DataInputStream 数据字节输入流
- PushbackInputStream 推回字节输入流
- LineNumberInputStream 已过时,使用LineNumberReader替代
FilterOutputStream
- BufferedOutputStream 缓冲字节输出流
- DataOutputStream 数据字节输出流
- PrintStream(不会抛出IOException)打印字节流,System.err和System.out是该类的实例
abstract FilterReader
- PushbackReader 推回字符输入流
abstract FilterWriter java.io包中无对应的子类实现
除Filter相关类外,其余的处理流类如下:
abstract InputStream
- ObjectInputStream 对象字节输入流,与ObjectOutputStream配套使用
- SequenceInputStream 序列字节输入流,用于串行多个InputStream处理
abstract OutputStream
- ObjectOutputStream 对象字节输出流,与ObjectInputStream配套使用
abstract Reader
- BufferedReader 缓冲字符输入流
- LineNumberReader 按行读取字符输入流,是BufferedReader的子类
- InputStreamReader 字节流-字符流桥接类
abstract Writer
- BufferedWriter 缓冲字符输出流
- LineNumberWriter 按行读取字符输出流,是BufferedWriter的子类
- OutputStreamWriter 字节流-字符流桥接类
- PrintWriter 打印字符输出流,类方法不会抛出I/O Exception
功能划分
从功能需求的角度,java.io中提供的若干个类,在不考虑字节流和字符流区分的情况下,一些基本或特殊功能的使用原则如下:
基本分类
处理文件(可根据文件类型进行选择)
- FileInputStream
- FileOutputStream
- FileReader
- FileWriter
按行处理文件(按行处理文本数据)
- LineNumberReader
处理字节数组byte[]
- ByteArrayInputStream
- ByteArrayOutputStream
处理字符数组char[]
- CharArrayReader
- CharArrayWriter
处理字符串
- StringReader
- StringWriter
处理网络数据流(网络数据包括)
- InputStream
- OutputStream
- Reader
- Writer
格式化输出(格式化输出字节或字符数据) :
- PrintStream
- PrintWriter
使用缓冲功能(使用一块缓冲区域保存数据后一次写入以提高性能) :
- BufferedInputStream
- BufferedOuputStream
- BufferedReader
- BufferedWriter
字节流转字符流(字节流数据向字符流数据的转换媒介):
- InputStreamReader
- OutputStreamWriter
进程间通信:
- PipedInputStream 只能连接PipedOutputStream
- PipedOutputStream
- PipedReader 只能连接PipedWriter
- PipedWriter
推回输入流(多用于Parser场景,譬如编译器之类):
- PushbackInputStream
- PushbackReader
组合输入流(多个输入流的macro类):
- SequenceInputStream
补充说明
最后补充几个小笔记:
- PrintStream在JDK1.1时被标注Deprecated,又在JDK1.2时移除该标注,仅因为System.out使用PrintStream实现,从而导致该类无法被抛弃。
- OutputStream的flush方法会清空对应缓冲区。
- FileInputStream的finalize方法在FileInputStream对象被GC时进行调用,以确保文件在垃圾回收前被关闭。不需要手动调用finalize方法,但是如果继承了该类,则必须在子类中调用super.finalize()方法。
- 可以同时通过多个FileInputStream流处理同一文件,而输出流则不行(涉及同步等问题)。
- PrintStream可用来将输出流重定向,譬如写入一个文本等等。
- 虽然FilterInputStream和FilterOuputStream没有abstract修饰,但是其构造函数有protected修饰,故而只能通过子类创建对应对象。
本文舍弃了诸多类的方法、细节实现描述,假如一一叙述,篇幅则会过长,且并非本文目标。
如需了解详细内容,建议在使用的同时翻阅相应的源码、书籍,而不是一次性接收,否则,后续仍会遗忘其设计初衷。