Java IO流按处理数据单位分类
本文最后更新于 2025-11-13,文章内容可能已经过时。
Java IO流按处理数据单位分类 的内容进行更详细、系统化的梳理,全面理解字节流与字符流的体系结构、设计原理和使用场景。
一、Java IO流按处理数据单位分类
1. 字节流(Byte Streams)
处理单位:byte(8位),适用于所有类型的数据(文本、图片、音频、视频等)
核心抽象类:InputStream(输入)、OutputStream(输出)
(1)InputStream 及其常见子类
| 类名 | 类型 | 功能说明 |
|---|---|---|
| FileInputStream | 节点流(Node Stream) | 从文件中读取原始字节数据 |
| ByteArrayInputStream | 节点流 | 从内存中的 byte 数组读取数据,不涉及 I/O 设备 |
| PipedInputStream | 节点流 | 用于线程间通信,通常与 PipedOutputStream 配对使用 |
| SequenceInputStream | 节点流 | 将多个 InputStream 合并为一个连续的输入流 |
| BufferedInputStream | 处理流(Filter Stream) | 在内部维护缓冲区,减少底层 read() 调用次数,提高效率 |
| DataInputStream | 处理流 | 支持读取 Java 基本数据类型(如 int、double 等),需与 DataOutputStream 配对 |
| ObjectInputStream | 处理流 | 用于反序列化对象(读取通过 ObjectOutputStream 写入的对象) |
| PushbackInputStream | 处理流 | 允许“回退”已读取的字节(常用于语法分析器) |
| FilterInputStream | 抽象基类 | 所有处理流的父类,本身不提供功能,仅作为装饰器模式的基础 |
(2)OutputStream 及其常见子类
| 类名 | 类型 | 功能说明 |
|---|---|---|
| FileOutputStream | 节点流 | 向文件写入原始字节数据 |
| ByteArrayOutputStream | 节点流 | 将数据写入内存中的 byte 数组,可通过 toByteArray() 获取结果 |
| PipedOutputStream | 节点流 | 与 PipedInputStream 配合实现线程间管道通信 |
| BufferedOutputStream | 处理流 | 内部缓冲,延迟写入,提升性能(需注意 flush()) |
| DataOutputStream | 处理流 | 写入 Java 基本数据类型,格式与 DataInputStream 兼容 |
| ObjectOutputStream | 处理流 | 序列化对象到流中(对象必须实现 Serializable 接口) |
| PrintStream | 处理流 | 提供 print()/println() 方法,自动转换数据为字符串并写入(如 System.out) |
| FilterOutputStream | 抽象基类 | 所有字节输出处理流的父类 |
2. 字符流(Character Streams)
处理单位:char(16位 Unicode 字符),专为文本设计
核心抽象类:Reader(输入)、Writer(输出)
本质是字节流 + 编码转换
(1)Reader 及其常见子类
| 类名 | 类型 | 功能说明 |
|---|---|---|
| FileReader | 节点流 | 从文件读取字符,默认使用平台默认编码(不推荐,应显式指定编码) |
| StringReader | 节点流 | 从字符串中读取字符 |
| CharArrayReader | 节点流 | 从 char 数组读取字符 |
| PipedReader | 节点流 | 与 PipedWriter 配合用于线程间字符通信 |
| InputStreamReader | 转换流(桥梁) | 将字节流转换为字符流,可指定字符编码(如 UTF-8、GBK) |
| BufferedReader | 处理流 | 带缓冲的字符输入流,支持 readLine() 方法(高效读取文本行) |
| PushbackReader | 处理流 | 支持“回退”已读取的字符 |
| FilterReader | 抽象基类 | 字符输入处理流的通用父类 |
(2)Writer 及其常见子类
| 类名 | 类型 | 功能说明 |
|---|---|---|
| FileWriter | 节点流 | 向文件写入字符,默认平台编码(同样不推荐) |
| StringWriter | 节点流 | 将字符写入内部字符串缓冲区,可通过 toString() 获取结果 |
| CharArrayWriter | 节点流 | 将字符写入 char 数组 |
| PipedWriter | 节点流 | 与 PipedReader 配合使用 |
| OutputStreamWriter | 转换流(桥梁) | 将字符流转换为字节流,可指定编码(核心桥梁类) |
| BufferedWriter | 处理流 | 带缓冲的字符输出流,支持 newLine() 方法 |
| PrintWriter | 处理流 | 提供 print()/println(),可自动刷新,常用于日志或控制台输出 |
| FilterWriter | 抽象基类 | 字符输出处理流的通用父类 |
3. 转换流(Bridge Streams)--连接字节流与字符流的关键
| 类名 | 作用 | 构造方式 | 重要性 |
|---|---|---|---|
| InputStreamReader | 字节 → 字符 | new InputStreamReader(InputStream in, String charset) | 必须显式指定编码,否则使用平台默认编码(易乱码) |
| OutputStreamWriter | 字符 → 字节 | new OutputStreamWriter(OutputStream out, String charset) | 同上,确保写入时使用正确编码 |
✅ 最佳实践:
永远不要直接使用FileReader/FileWriter,而应使用:Reader reader = new InputStreamReader(new FileInputStream("file.txt"), "UTF-8"); Writer writer = new OutputStreamWriter(new FileOutputStream("file.txt"), "UTF-8");
二、补充说明(深度扩展)
1. 为什么需要字符流?--Unicode 与编码问题
- Java 内部使用 Unicode:每个
char占 2 字节(UTF-16 编码),但外部文件可能使用 GBK、UTF-8、ISO-8859-1 等编码。 - 字节流无法理解字符边界:例如 UTF-8 中一个中文字符占 3 字节,若用
FileInputStream逐字节读取,会把一个汉字拆成 3 个“乱码字节”。 - 字符流自动处理编码转换:通过
InputStreamReader,它知道如何将字节序列按指定编码解析为合法的 Unicode 字符。
📌 举例:
文件内容为“你好”,UTF-8 编码下占 6 字节。
- 字节流读取:得到 6 个无意义的 byte 值(如 -28, -67, -96, ...)
- 字符流读取:得到 2 个 char:'你'、'好'
2. 字节流 vs 字符流:核心区别总结
| 维度 | 字节流 | 字符流 |
|---|---|---|
| 处理单位 | byte(8位) | char(16位 Unicode) |
| 适用数据 | 任意二进制数据(图片、视频、文本等) | 仅限文本数据 |
| 编码敏感 | 否(原始字节) | 是(必须指定或默认编码) |
| 抽象基类 | InputStream / OutputStream | Reader / Writer |
| 是否能处理中文 | 不能直接处理(会乱码) | 能(只要编码匹配) |
| 性能 | 低层、高效 | 高层、带编码开销 |
| 典型用途 | 文件复制、网络传输、序列化 | 读写配置文件、日志、CSV、JSON 等文本 |
3. 使用建议(实战经验)
- 不确定数据类型?用字节流:如上传文件、下载资源,统一用 InputStream/OutputStream。
- 明确是文本?用字符流 + 显式编码:避免 FileReader,改用 InputStreamReader(..., "UTF-8")。
- 大量读写?加缓冲:BufferedInputStream / BufferedReader 可提升 10~100 倍性能。
- 读写基本类型?用 Data 流:如保存游戏存档(int 分数、double 时间)。
- 对象持久化?用 Object 流:但注意版本兼容性和安全性问题。
- 多文件合并?用 SequenceInputStream(字节)或自定义 Reader(字符)。
4. 缓冲流的工作原理
-
无缓冲:每次 read() 都触发一次系统调用(如磁盘 I/O),开销大。
-
有缓冲:一次性读取 8KB(默认)到内存缓冲区,后续 read() 从缓冲区取,直到用完再触发下一次 I/O。
-
性能对比:
// 慢:每次读1字节,100万次系统调用 FileInputStream fis = new FileInputStream("big.txt"); // 快:1次读8192字节,约122次系统调用 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("big.txt"));
⚠️ 注意:
BufferedOutputStream和PrintWriter默认不自动 flush,写完记得调用flush()或close()!
5. 装饰器模式在 IO 流中的体现
Java IO 是装饰器模式(Decorator Pattern) 的经典应用:
// 层层包装:节点流 → 转换流 → 缓冲流
Reader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("data.txt"),
"UTF-8"
)
);
- 每一层只关注自己的职责(读文件、转编码、加缓冲)
- 灵活组合,避免类爆炸(不用为每种组合创建新类)
三、总结图示(逻辑结构)
字节流体系:
InputStream ← FileInputStream, ByteArrayInputStream, ...
← FilterInputStream ← BufferedInputStream, DataInputStream, ...
OutputStream ← FileOutputStream, ByteArrayOutputStream, ...
← FilterOutputStream ← BufferedOutputStream, DataOutputStream, ...
字符流体系:
Reader ← FileReader (慎用), StringReader, CharArrayReader, ...
← InputStreamReader (桥梁!) ← 包装 InputStream
← BufferedReader, PushbackReader, ...
Writer ← FileWriter (慎用), StringWriter, CharArrayWriter, ...
← OutputStreamWriter (桥梁!) ← 包装 OutputStream
← BufferedWriter, PrintWriter, ...
这份详细解析彻底掌握 Java IO 流的分类与使用逻辑!如有特定场景(如网络编程、文件加密、大文件处理等),也可以进一步探讨优化方案。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 软件从业者Hort
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果

