Java IO 是 Java 的一套 API, 用于读入和写出数据(输入和输出)。Java IO API 位于 java.io package。实际上 java.io package 没有解决所有的输入输出情况,例如,web 页面没有包含在 Java IO package 里面,而是由 Java 企业版本的 servlet 和 HTTP package 处理。
Java IO package 关注文件、网络流(Network Stream), 内存缓冲区(internal memory buffer)的输入输出。Java IO package 没有包含用于打开网络套接字(Network socket) 的类。网络套接字由 Java Network API 打开,然后 Java IO 的 InputStream / OutputStream 从打开的套接字读入,或者向打开的套接字写出数据。
Java NIO 用于处理非阻塞的输入输出操作。
Input 和 Output
最常见的数据源或目的地有:
文件
管道
网络连接
内存缓冲区
System.in, System.out, System.error
本质的数据流动请求如下
Source -> Program -> Destination
Stream
Stream 是 Java IO 的核心概念。Stream 是一个无尽头的数据流,可以从一个 Stream 读取数据,也可以向一个 Stream 写入数据。Stream 没有下标,也不能向前向后地移动,和数组不一样。
Stream 可以简单分为基于字节(byte based),或者基于字符(character based)。
基于字节的 stream 一般叫做 xxxxxxStream,例如 InputStream, OutputStream。这些 stream 一次只读入或写出一个字节,除了 DataInputStream / DataOutputStream。DataInputStream / DataOutputStream 用于处理 int / long / boolean 等类型。
基于字符的 stream 一般叫做 xxxxxxReader 或 xxxxxxWriter。
程序通过 InputStream 或者 Reader 读入数据,通过 OutputStream 或者 Writer 写出数据。
Source -> InputStream / Reader -> Program
Program -> OutputStream / Writer -> Destination
如果需要写组件用于输入或输出,尽量让组件依赖于 InputStream / OutputSteam,而不是他们的具体子类,使得代码更加灵活应变。例如
InputStream is = new FileInputStream(filepath);
OutputSteam os = new FileOutputStream(filepath);
一次从文件读取一个字节是非常慢的,使用 BufferedInputStream 一次读取一大块数据会快很多。例子
InputStream is = new BufferedInputStream(new FileInputStream("/tmp/tony/a.txt"));
缓冲(buffer) 也适用于 OutputStream,用于批量写出到硬盘。
Byte Based | Character Based | ||||
Input | Output | Input | Output | comment | |
Basic | InputStream | OutputStream |
Reader InputStreamReader |
Writer OutputStreamWriter |
|
Arrays | |||||
Files | FileInputStream | FileOutputStream | FileReader | FileWriter | |
Pipes | |||||
Buffering | BufferedInputStream | BufferedOutputStream | In memory, better performance | ||
Filter | |||||
Parsing | PushbackInpustream | PushbackReader | |||
String | |||||
Data | |||||
Data-formated | |||||
Object | |||||
Utilities |
InputStream 和 OutputStream
InputStream 是 Java IO API 中所有 input stream 的基础类。InputSream 下面的子类有 FileInputStream, BufferedInputStream。 InputStream 例子
InputStream is = new FileInputStream("/tmp/tony/a.txt");
int data = is.read();
while(data != -1){
System.out.print((char)data);
data = is.read();
}
FileInputStream 是 InputStream 的子类。例子中省略了异常的处理。
当 read() 方法返回 -1 时,说明 stream 以及没有内容可以读取了。-1 是 int 值,而不是 byte 或 short 值。
read(byte[]) 方法一次读取字节数组,返回读入的字节数。read(byte[]) 比一次只读取一个字节的 read() 要快。每次都需要检查 read(byte[]) 的返回值,来了解读取的数据大小。
mark() 和 reset() 方法主要用于实现解释器,用到情况小。
OutputStream 是 Java IO API 中所有 output stream 的基础类。OutputStream 下面的子类有 FileOutputStream, BufferedOutputStrea。OutputStream 例子
OutputStream os = new FileOutputStream("/tmp/tony/b.txt");
byte[] b = {'h', 'e', 'l', 'l'};
os.write(b);
os.close();
write(byte[]) 方法将 byte 数组中的所有字节写入到 OutputStream 中
flush() 方法,将已经写出 OutputSteam 的数据清刷(写出)到底层的数据目的地。例如,如果 OutputStream 是 FileOutputStream,写出到 FileOutputStream 的字节还没有全部写出到硬盘。即使 java 代码已经将数据写出到 FileOutputStream,数据可能还在缓存区而已。通过 flush() 方法,可以确保数据都清刷到硬盘(或网络)。
close() 方法,用于关闭 stream。通常位于 final 代码块。
Reader 和 Writer
Reader 和 Writer 在 Java IO 中的功能类似于 InputStream 和 OutputStream,差异点在于 Reader 和 Writer 基于字符。
Reader 是 Java IO API 中又有 reader 的基础类。Reader 下面的子类有 BufferedReader, InputStreamReader, StringReader。例子
Reader reader = new FileReader("/tmp/tony/a.txt");
int data = reader.read();
while(data != -1){
System.out.print((char)data);
data = reader.read();
}
InputStream 一次返回 8 bit( 一个 byte ) 的值 (0 到 255)
Reader 一次返回 16 bit 的值 ( 0 到 65535), 可能是一个或多个 byte,取决于文本采用的编码格式。
Reader 可以套在 InputStream 外面,来读取字符。
InputStream is = new FileInputStream("/tmp/tony/a.txt");
Reader rd = new InputStreamReader(is);
Writer 可以类似地套在 OutputStream 外面,写出字符。
OutputStream os = new FileOutputStream("/tmp/tony/b.txt");
Writer wt = new OutputStreamWriter(os);
wt.write("qqq");
wt.close();
Reader / Writer 子类的的组装
Reader reader = new BufferedReader(new FileReader("/tmp/tony/a.txt")); Writer writer = new BufferedWriter(new FileWriter("/tmp/tony/c.txt"));
参考资料