掌握FilterInputStream包装模式的关键在于理解“一层包一层、功能可叠加”的设计逻辑,其本质是委托而非继承,子类如BufferedInputStream、DataInputStream、GZIPInputStream分别实现缓冲、类型解析、解压等功能,多层包装按从外到内顺序执行,组合时需注意语义兼容性,自定义过滤流需继承FilterInputStream并重写read()等方法。
掌握filterinputstream包装模式的关键,不在于死记类名,而在于理解“一层包一层、功能可叠加”的设计逻辑。它让同一个inputstream变量,在不改变类型声明的前提下,动态获得缓冲、数据解析、压缩解密等能力。
理解包装的本质:不是继承,是委托
FilterInputStream本身不做实际读取,只是持有一个底层InputStream引用(protected volatile InputStream in),所有read()、close()等方法都直接转发给这个in。真正加功能的是它的子类,比如:
- BufferedInputStream:内部维护byte[]缓冲区,一次预读多字节,减少系统调用次数
- DataInputStream:提供readInt()、readUTF()等方法,把字节按Java基本类型规则解释
- GZIPInputStream:在读取时自动解压gzip格式数据,上层代码仍用普通read()
多层包装的写法与执行顺序
包装顺序决定处理流程,从外向内逐层生效。例如:
InputStream fis = new FileInputStream("data.bin");
InputStream bis = new BufferedInputStream(fis); // 先缓冲
InputStream dis = new DataInputStream(bis); // 再支持读int
当调用dis.readInt()时,实际执行路径是:DataInputStream → BufferedInputStream → FileInputStream。每层只关心自己该做的事:DataInputStream负责拼4个字节转int,BufferedInputStream负责从缓冲区拿数据或触发底层填充,FileInputStream才真正访问磁盘。
常见组合场景与注意事项
不同过滤流组合需注意语义兼容性:
- 缓冲+数据读取:推荐
new DataInputStream(new BufferedInputStream(...)),避免每次readInt()都触发单字节磁盘IO - 压缩+缓冲:应先压缩后缓冲(
new BufferedInputStream(new GZIPInputStream(...))),否则缓冲的是压缩后的乱码字节 - 加密流(如CipherInputStream)通常放在最外层,保证解密后数据再交由其他过滤器处理
- 所有包装流都必须显式close(),但只需关闭最外层——因为close()会逐层委托到底层节点流
自定义过滤流的切入点
若标准类不满足需求,可继承FilterInputStream实现自己的逻辑。关键点有二:
- 构造方法中必须传入并保存一个InputStream,这是装饰的前提
- 重写read()等方法时,优先调用
super.read()或in.read()获取原始数据,再做加工(如日志记录、格式校验、字节替换)
例如,一个简单计数过滤流可在每次read()后递增字节数统计,无需改动底层行为,也不影响上层调用方式。


















