本文最后更新于 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 / OutputStreamReader / 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"));
    

⚠️ 注意:BufferedOutputStreamPrintWriter 默认不自动 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 流的分类与使用逻辑!如有特定场景(如网络编程、文件加密、大文件处理等),也可以进一步探讨优化方案。