IO 流的分类
打开 Java API 文档,满屏都是流的名字:
FileInputStream、BufferedOutputStream、InputStreamReader、DataInputStream、ObjectInputStream... 到底该用哪个?
这个问题背后,是对 IO 流分类体系的误解。只要把分类逻辑理清,选流就像查字典一样简单。
按数据单位分:字节流 vs 字符流
字节流
处理最原始的二进制数据,InputStream / OutputStream 是两个根类:
java
// 字节输入
InputStream in = new FileInputStream("data.bin");
int b = in.read(); // 返回 0~255 或 -1
// 字节输出
OutputStream out = new FileOutputStream("data.bin");
out.write(65); // 写入字节 65('A' 的 ASCII 码)图片、音频、视频、压缩包、所有二进制文件——用字节流处理。
字符流
处理文本数据,Reader / Writer 是两个根类:
java
// 字符输入
Reader reader = new FileReader("text.txt");
int c = reader.read(); // 返回 Unicode 码点(0~65535)
// 字符输出
Writer writer = new FileWriter("text.txt");
writer.write("你好世界"); // 直接写字符串文本文件(.txt、.java、.csv、.json)——用字符流处理。
两者关系:字符流底层是字节流,读写时自动做字符编码转换。
按流向分:输入流 vs 输出流
流向是相对于程序而言的:
输入流:从外部(文件/网络/键盘)读入程序
输出流:从程序写出去(文件/网络/屏幕)一个经典混淆:
java
// ❌ FileInputStream 不是「文件输入到程序」,而是「文件内容进入程序」
FileInputStream fis = new FileInputStream("data.txt");
// ↑ 这叫「文件输入流」:从文件读入程序
// ❌ FileOutputStream 不是「文件输出」,而是「程序内容输出到文件」
FileOutputStream fos = new FileOutputStream("data.txt");
// ↑ 这叫「文件输出流」:从程序写出到文件记住:输入/输出永远是相对于当前程序的位置。
按功能分:节点流 vs 处理流
这是 Java IO 最核心的分类。
节点流(Node Stream)
直连数据源的流,就像直接拿着水管接到水源上:
java
FileInputStream fis = new FileInputStream("data.txt");
// ↑ 直连文件,没有中间处理环节处理流(Processing Stream / Filter Stream)
包装节点流,在数据流动过程中做加工,就像在水管中间加个过滤器:
java
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("data.txt"));
// ↑ 包装 FileInputStream,加了缓冲功能
// 实际数据源还是文件,但数据会先进入缓冲区处理流也叫装饰器模式:可以在不改变原有流的情况下,给它添加新功能。
常见处理流速查
| 处理流 | 包装的节点流 | 功能 |
|---|---|---|
BufferedInputStream | FileInputStream | 缓冲,减少系统调用 |
BufferedOutputStream | FileOutputStream | 缓冲,批量写出 |
BufferedReader | FileReader | 缓冲 + readLine() |
BufferedWriter | FileWriter | 缓冲 + newLine() |
DataInputStream | InputStream | 读写基本数据类型 |
DataOutputStream | OutputStream | 写入基本数据类型 |
ObjectInputStream | InputStream | 反序列化对象 |
ObjectOutputStream | OutputStream | 序列化对象 |
InputStreamReader | InputStream | 字节流转字符流(指定编码) |
OutputStreamWriter | OutputStream | 字符流转字节流(指定编码) |
PrintStream | OutputStream | 格式化打印,不抛异常 |
PrintWriter | Writer | 字符版格式化打印 |
装饰器模式
Java IO 的处理流都遵循装饰器(Decorator)设计模式:
DataInputStream(BufferedInputStream(FileInputStream("file.txt")))
↑ 第四层:读基本类型
↑ 第三层:加缓冲
↑ 第二层:加缓冲
↑ 第一层:直连文件核心特点:装饰器和被装饰者实现同一个接口,所以可以无限嵌套。
java
// ❌ 节点流直连:每次 read() 可能触发一次系统调用
InputStream in = new FileInputStream("big.dat");
// ✅ 层层包装:加缓冲、加类型转换
DataInputStream dis = new DataInputStream(
new BufferedInputStream(
new FileInputStream("big.dat"), 64 * 1024));分类总图
┌──────────────────────────────┐
│ 数据单位 │
│ 字节流 字符流 │
│ InputStream Reader │
│ OutputStream Writer │
└──────────────────────────────┘
│
┌─────────┴─────────┐
│ 流向 │
输入流 输出流
(Reader/ (Writer/
InputStream) OutputStream)
│
┌─────────┴─────────┐
│ 功能 │
节点流 处理流
直连数据源 包装节点流
FileInputStream BufferedInputStream
FileOutputStream DataInputStream
FileReader ObjectInputStream
FileWriter InputStreamReader本节要点
- 按单位:字节流处理二进制,字符流处理文本(底层还是字节)
- 按流向:输入流进程序,输出流出程序
- 按功能:节点流直连数据源,处理流包装节点流(装饰器模式)
- 处理流可以无限嵌套,关闭最外层即可全部关闭
下一步
- IO 流体系结构 — 核心类的继承关系图
- 阻塞IO vs 非阻塞IO — read() 会不会卡住
