filterinputstream 是装饰器模式的核心抽象类,通过包装底层 inputstream 并重写 read() 等方法实现功能增强;自定义子类必须委托调用 super 方法,再注入逻辑,不可跳过委托或在构造时预读数据。

FilterInputStream 是 Java IO 中实现装饰器模式的核心抽象类,它本身不直接处理数据,而是包装一个底层 InputStream,并在其基础上添加额外功能。要扩展自定义流功能,关键不是继承它“替代”原有行为,而是**在它的子类中重写特定方法(尤其是 read() 系列),在调用被包装流的基础上插入逻辑**。
理解 FilterInputStream 的装饰本质
FilterInputStream 构造时接收一个 InputStream,所有读取操作默认委托给它。它本身是“透明”的——你创建 new BufferedInputStream(new FileInputStream(...)),实际调用的是 BufferedInputStream 重写的 read 方法,而该方法内部会先查缓冲区、缺数据时才调用底层 FileInputStream 的 read。所以自定义装饰器必须遵循这个委托+增强的逻辑。
重写 read() 方法注入自定义逻辑
这是最常用且最有效的扩展点。例如实现一个统计已读字节数的流:
public class CountingInputStream extends FilterInputStream {
private long count = 0;
public CountingInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int b = super.read(); // 委托给底层流
if (b != -1) count++; // 仅当读到有效字节才计数
return b;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int n = super.read(b, off, len); // 委托
if (n > 0) count += n;
return n;
}
public long getCount() { return count; }
}
注意:必须调用 super.read() 完成实际读取,再做自己的事;不能跳过委托,否则就不是装饰器而是全新实现。
按需重写其他方法保持一致性
如果自定义逻辑影响流的状态或语义,相关方法也要同步处理。比如实现一个“跳过 BOM 头”的 UTF-8 流:
- 在构造时尝试读取并识别 UTF-8 BOM(
EF BB BF),若存在则内部 consume 掉 - 重写
available():减去已跳过的 BOM 字节数 - 重写
mark()/reset():确保 mark 位置包含 BOM 判断逻辑 - 不要重写 close():除非需要释放额外资源,否则直接委托即可
避免常见误区
- 不要在构造函数里一次性读完所有数据——这违背流的延迟读取特性,也破坏装饰器的“按需增强”原则
- 不要覆盖
in字段或绕过 super 调用——那就不再是装饰,而是替换 - 线程安全需自行保证:FilterInputStream 本身不加锁,若装饰逻辑涉及共享状态(如计数器),应使用
AtomicLong或同步块
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











