Java IO通过装饰者模式动态扩展流功能:以InputStream等为抽象组件,FileInputStream为具体组件,FilterInputStream为装饰器基类,BufferedInputStream等为具体装饰器,通过组合而非继承增强行为。

Java 中用装饰者模式扩展 IO 流功能,核心是在不修改原始流类的前提下,通过组合方式动态增强行为。比如给 FileInputStream 增加缓冲、编码转换或行号追踪能力,不是靠继承,而是用 BufferedInputStream、InputStreamReader、LineNumberInputStream(已过时,但原理仍适用)等装饰器层层包装。
理解装饰者模式在 IO 中的结构
Java IO 包(java.io)是装饰者模式的经典实践:
-
抽象组件(Component):如
InputStream、OutputStream、Reader、Writer—— 定义基础操作(read()、write()等) -
具体组件(Concrete Component):如
FileInputStream、ByteArrayInputStream—— 提供最底层数据源/目标 -
装饰器抽象(Decorator):同样继承/实现抽象组件(如
FilterInputStream继承InputStream),内部持有一个组件引用 -
具体装饰器(Concrete Decorator):如
BufferedInputStream、CheckedInputStream、Base64InputStream(第三方)—— 在调用被装饰对象前后添加逻辑
手写一个实用装饰器:自动 UTF-8 解码的 InputStream
标准 InputStream 不处理字符编码,InputStreamReader 是它的装饰器。我们可以仿照它写一个轻量版:
public class Utf8InputStream extends InputStream {
private final InputStream in;
private final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
private final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
private CharBuffer charBuffer = CharBuffer.allocate(512);
public Utf8InputStream(InputStream in) {
this.in = in;
}
@Override
public int read() throws IOException {
// 实际项目中更推荐按块解码 + 缓存;此处简化示意逻辑
// 真实场景建议直接用 InputStreamReader,本例重在展示装饰思路
throw new UnsupportedOperationException("单字节 read() 不适合字符流,应使用 read(byte[])");
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int n = in.read(b, off, len);
if (n > 0) {
byteBuffer.clear().put(b, off, n).flip();
charBuffer.clear();
decoder.decode(byteBuffer, charBuffer, true);
charBuffer.flip();
// 此处可对 charBuffer 做预处理(如过滤控制字符),再转回 byte[]
// 即实现了「装饰后的行为增强」
}
return n;
}
@Override
public void close() throws IOException {
in.close();
}
}
重点在于:构造时接收原始流,所有方法都委托给它,并在关键节点插入新逻辑。真实开发中优先复用 JDK 装饰器(如 new BufferedReader(new InputStreamReader(new FileInputStream(...), "UTF-8")))。
立即学习“Java免费学习笔记(深入)”;
典型组合链与功能叠加
装饰者支持任意嵌套,每层专注单一职责:
-
new DataInputStream(new BufferedInputStream(new FileInputStream("data.bin")))
→ 缓冲读取 + 支持读取 int/long 等基本类型 -
new LineNumberReader(new BufferedReader(new FileReader("log.txt")))
→ 行缓冲 + 自动统计当前行号(注意:JDK 9+ 推荐用BufferedReader的lines()流式处理) -
new GZIPInputStream(new FileInputStream("file.gz"))
→ 解压缩 + 原始字节流,无缝对接后续装饰器
这种组合方式让 IO 流具备高度灵活性:同一份文件输入,可按需装配「缓冲 → 解压 → 编码转换 → 行号追踪」等功能,且各层解耦、可替换、可测试。
注意事项和现代替代方案
装饰者模式虽强大,但要注意:
-
避免过度嵌套:5 层以上装饰可能影响可读性和调试效率;考虑封装成工具方法(如
Files.newBufferedReader(path, UTF_8)) -
NIO.2 更简洁:Java 7+ 推荐用
Files工具类和Path,例如Files.lines(path, UTF_8)直接返回Stream<String>,底层已封装装饰逻辑 -
资源管理:装饰器链中只需关闭最外层流(如
BufferedReader),它会自动委托关闭内层(前提是装饰器正确实现close()) -
不要混淆装饰与适配:装饰器扩展行为(加功能),适配器转换接口(改签名);
InputStreamReader是装饰器(增强 InputStream 的字符能力),不是适配器


















