IO 流体系结构
Java 有几十个 IO 类,继承关系看似复杂,其实只有两条主线:字节流和字符流。搞清楚继承关系,你就能举一反三——学会一个类,其他类自然就会了。
字节流体系
java.lang.Object
│
└── java.io.InputStream(抽象类)
│
├── FileInputStream 文件字节输入流(节点流)
├── ByteArrayInputStream 字节数组输入流(节点流)
├── PipedInputStream 管道输入流(线程间通信)
├── FilterInputStream 过滤输入流(装饰器基类)
│ ├── BufferedInputStream 加缓冲
│ ├── DataInputStream 读基本类型
│ └── ObjectInputStream 读对象
└── SequenceInputStream 合并多个输入流java.lang.Object
│
└── java.io.OutputStream(抽象类)
│
├── FileOutputStream 文件字节输出流(节点流)
├── ByteArrayOutputStream 字节数组输出流(节点流)
├── PipedOutputStream 管道输出流(线程间通信)
├── FilterOutputStream 过滤输出流(装饰器基类)
│ ├── BufferedOutputStream 加缓冲
│ ├── DataOutputStream 写基本类型
│ ├── ObjectOutputStream 写对象
│ └── PrintStream 打印流(最常用)
└── StreamTokenizer 字符串解析器字符流体系
java.lang.Object
│
└── java.io.Reader(抽象类)
│
├── InputStreamReader 字节→字符转换流
│ └── FileReader 文件字符输入流(便捷类,但默认编码有坑)
├── CharArrayReader 字符数组输入流(节点流)
├── StringReader 字符串输入流(节点流)
├── PipedReader 管道字符输入流
├── BufferedReader 缓冲字符输入流(处理流 + readLine())
└── LineNumberReader 带行号的 BufferedReaderjava.lang.Object
│
└── java.io.Writer(抽象类)
│
├── OutputStreamWriter 字符→字节转换流
│ └── FileWriter 文件字符输出流(便捷类,但默认编码有坑)
├── CharArrayWriter 字符数组输出流(节点流)
├── StringWriter 字符串输出流(节点流)
├── PipedWriter 管道字符输出流
├── BufferedWriter 缓冲字符输出流(处理流 + newLine())
└── PrintWriter 打印流(字符版,比 PrintStream 更推荐)NIO 体系
java.nio.channels.Channel(接口)
│
├── SelectableChannel(可注册到 Selector)
│ ├── SocketChannel TCP 客户端通道
│ ├── ServerSocketChannel TCP 服务端通道
│ ├── DatagramChannel UDP 通道
│ └── Pipe.SinkChannel / Pipe.SourceChannel 管道通道
└── FileChannel 文件通道(不能注册到 Selector)java.nio.Buffer(抽象类)
│
├── ByteBuffer 字节缓冲区(最常用)
├── CharBuffer 字符缓冲区
├── ShortBuffer / IntBuffer / LongBuffer
├── FloatBuffer / DoubleBuffer
└── MappedByteBuffer 内存映射缓冲区装饰器模式:为什么类这么多
Java IO 类众多的核心原因:装饰器模式。
核心思想:把功能一层一层地「包」上去。
java
// 基础版:能跑,但慢
InputStream in = new FileInputStream("data.bin");
// 加缓冲:减少系统调用,快几十倍
InputStream in = new BufferedInputStream(
new FileInputStream("data.bin"));
// 加数据类型:能读 int/double
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("data.bin")));
// 加对象:能读对象
ObjectInputStream in = new ObjectInputStream(
new BufferedInputStream(
new FileInputStream("data.bin")));每个「装饰器」只加一个功能,可以自由组合。
最常用的组合
| 场景 | 推荐组合 |
|---|---|
| 读二进制文件 | BufferedInputStream + FileInputStream |
| 写二进制文件 | BufferedOutputStream + FileOutputStream |
| 读文本文件 | BufferedReader + InputStreamReader(指定 UTF-8) |
| 写文本文件 | BufferedWriter + OutputStreamWriter(指定 UTF-8) |
| 读对象 | ObjectInputStream + BufferedInputStream |
| 打印日志 | PrintWriter 或 PrintStream |
记住这个口诀:字节字符是两条线,节点处理是装饰器,永远记得包 Buffered。
下一步
- 阻塞 vs 非阻塞 IO — read() 什么时候会卡住
