Java IO流是装饰器模式的经典实践,通过Component(InputStream等抽象类)、ConcreteComponent(FileInputStream等)、Decorator(FilterInputStream等)和ConcreteDecorator(BufferedInputStream等)四类角色实现功能解耦与自由组装。

Java IO 流是装饰器模式最经典、最直观的工业级实践。它不靠抽象说教,而是用一层套一层的包装,把“基础能力”和“增强功能”彻底解耦——你拿到的不是某个固定功能的流,而是一个可自由组装的功能链。
核心结构:四类角色在 IO 中的真实映射
理解装饰器模式,关键在于看清每个角色在 java.io 包里的具体体现:
-
Component(抽象构件):对应
InputStream、OutputStream、Reader、Writer这些抽象基类。它们只定义统一行为(如read()、write()、close()),不关心数据从哪来、怎么优化。 -
ConcreteComponent(具体构件):比如
FileInputStream、ByteArrayInputStream、SocketInputStream。它们负责最原始的数据源接入,不做任何加工。 -
Decorator(抽象装饰器):就是
FilterInputStream和它的兄弟类FilterOutputStream、FilterReader、FilterWriter。它们继承自对应基类,并持有一个被包装流的引用(protected InputStream in),所有方法默认委托给内部流执行。 -
ConcreteDecorator(具体装饰器):例如
BufferedInputStream(加缓冲)、GZIPInputStream(解压缩)、ObjectInputStream(反序列化)、LineNumberReader(行号计数)。每个类只专注一件事,通过构造函数接收上游流,再叠加自身逻辑。
典型代码链:装饰是如何“叠”出来的
装饰的本质是构造时的组合,不是运行时的配置。每层包装都发生在实例化那一刻:
InputStream fis = new FileInputStream("data.gz");
InputStream bis = new BufferedInputStream(fis); // 第一层:缓冲
InputStream gis = new GZIPInputStream(bis); // 第二层:解压
InputStream dis = new DataInputStream(gis); // 第三层:支持读取基本类型
也可以写成单行嵌套形式,更体现“包裹感”:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
InputStream input = new DataInputStream(
new GZIPInputStream(
new BufferedInputStream(
new FileInputStream("data.gz")
)
)
);
调用 input.readInt() 时,请求会逐层向下传递:DataInputStream → GZIPInputStream → BufferedInputStream → FileInputStream,每一层在转发前或后插入自己的逻辑(如解压字节、填充缓冲区、解析二进制格式)。
为什么不用继承?装饰器解决了什么痛点
如果全靠继承扩展,面对 N 种数据源(文件、网络、内存、管道)和 M 种功能(缓冲、加密、压缩、校验、编码转换),子类数量将是 N × M 级别。而装饰器让组合爆炸变为线性增长:
- 新增一种数据源(如
ZipEntryInputStream),只需实现InputStream接口,无需为每种功能重写一遍; - 新增一种功能(如
AESInputStream),只需继承FilterInputStream,就能装饰任意已有流; - 功能可插拔:同一份业务代码,传入
BufferedInputStream或直接传FileInputStream,接口完全一致,仅性能不同。
自己动手写一个装饰器:加密输入流示例
掌握原理最快的方式是仿写。下面是一个轻量级的 XOR 加密装饰器:
public class XorInputStream extends FilterInputStream {
private final int key;
public XorInputStream(InputStream in, int key) {
super(in);
this.key = key;
}
@Override
public int read() throws IOException {
int b = super.read();
return (b == -1) ? -1 : b ^ key;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int n = super.read(b, off, len);
if (n > 0) {
for (int i = 0; i < n; i++) {
b[off + i] ^= key;
}
}
return n;
}
}
使用方式与其他装饰器完全一致:
InputStream plain = new FileInputStream("secret.txt");
InputStream encrypted = new XorInputStream(plain, 0x5A);
// 后续读取自动解密,对上层透明
这个例子展示了装饰器的核心契约:保持接口不变,只改变行为细节。

















