Skip to content

IO 流的分类

打开 Java API 文档,满屏都是流的名字:

FileInputStreamBufferedOutputStreamInputStreamReaderDataInputStreamObjectInputStream... 到底该用哪个?

这个问题背后,是对 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,加了缓冲功能
//     实际数据源还是文件,但数据会先进入缓冲区

处理流也叫装饰器模式:可以在不改变原有流的情况下,给它添加新功能。

常见处理流速查

处理流包装的节点流功能
BufferedInputStreamFileInputStream缓冲,减少系统调用
BufferedOutputStreamFileOutputStream缓冲,批量写出
BufferedReaderFileReader缓冲 + readLine()
BufferedWriterFileWriter缓冲 + newLine()
DataInputStreamInputStream读写基本数据类型
DataOutputStreamOutputStream写入基本数据类型
ObjectInputStreamInputStream反序列化对象
ObjectOutputStreamOutputStream序列化对象
InputStreamReaderInputStream字节流转字符流(指定编码)
OutputStreamWriterOutputStream字符流转字节流(指定编码)
PrintStreamOutputStream格式化打印,不抛异常
PrintWriterWriter字符版格式化打印

装饰器模式

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

本节要点

  • 按单位:字节流处理二进制,字符流处理文本(底层还是字节)
  • 按流向:输入流进程序,输出流出程序
  • 按功能:节点流直连数据源,处理流包装节点流(装饰器模式)
  • 处理流可以无限嵌套,关闭最外层即可全部关闭

下一步

基于 VitePress 构建