最直接有效的方式是使用装饰器模式。它不侵入原始类、不依赖继承,通过包装对象在运行时叠加行为;java io 中 fileinputstream 被 bufferedinputstream、zipinputstream 逐层装饰即为典型应用,强调初始化状态、装饰顺序、接口复用及动态装配,并优于代理与继承方案。

最直接有效的方式是使用装饰器模式。它不侵入原始类,也不依赖继承,而是通过包装对象,在运行时叠加新行为。
用装饰器模式包装流对象
Java IO 是经典范例:FileInputStream 提供基础字节读取能力,BufferedInputStream 不修改它,只将其作为构造参数传入,就实现了缓冲功能。ZipInputStream 同理,套在 BufferedInputStream 外层,就能支持 ZIP 解压——所有增强都发生在包装过程中,原始类完全不动。
- 必须保证被装饰流已初始化且未关闭,否则构造时虽不报错,首次 read() 就会抛异常
- 装饰顺序很重要:缓冲(BufferedInputStream)必须在解压缩(ZipInputStream)之前,否则性能极差甚至失败
- 网络流、文件流、内存流等不同来源,只要实现 InputStream 接口,都能被同一套装饰器复用
结合接口 default 方法补充通用逻辑
如果流处理对象实现了统一接口(如 Processor
- 方法必须通过接口引用调用,才能触发多态分派;不能在实现类内部硬调用 this.xxx()
- 避免多个接口提供同签名 default 方法,否则编译失败,需显式重写委托
- 可搭配抽象钩子方法(如 default boolean shouldLog() { return true; }),让各实现类按需定制
运行时动态装配装饰链
不必硬编码嵌套结构。可通过配置或工厂类构建装饰链,例如:
- 读取配置项 ["buffer", "gzip", "trace"],依次创建对应装饰器实例
- 用 try-with-resources 管理整个链的生命周期,确保底层流和所有装饰器正确关闭
- 装饰器自身也应实现 AutoCloseable,并在 close() 中调用被装饰对象的 close()
慎用代理或继承替代方案
代理模式(如 JDK 动态代理)虽也能拦截方法调用,但要求目标类实现接口,且对流这类以模板方法为主的类(如 InputStream 的 read() 系列)容易绕过关键逻辑;继承则受限于 final 类(如 FileInputStream)且易引发类爆炸。装饰器天然适配流的“组合优于继承”特性,是更自然的选择。











