缓冲流是装饰器模式的典型应用,通过动态组合实现功能增强而非继承扩展。它不管理数据源,仅专注提升I/O效率,依赖被包装流并委托操作,支持多层嵌套如缓冲+解压、缓冲+转码等,关闭外层即关闭全部,且避免类爆炸问题。

Java IO 流中缓冲流(如 BufferedInputStream、BufferedReader)是装饰器模式最常用、最直观的落地场景。它的灵活应用不在于“能不能加缓冲”,而在于“按需叠加、自由组合、互不干扰”——同一份底层数据源,可以被不同功能的装饰器按业务需要动态包裹,且彼此解耦。
缓冲流作为装饰器的核心定位
缓冲流本身不是独立的数据源,而是纯粹的功能增强层:
- 它不负责打开文件、连接网络或分配内存,只专注“减少系统调用次数、提升读写吞吐”这一件事;
- 它必须接收一个已存在的流(如
FileInputStream或InputStreamReader)作为构造参数,形成“被包装对象”的引用; - 所有读写方法(如
read()、write())默认委托给内部流执行,仅在关键节点插入缓冲逻辑(例如:BufferedInputStream在fill()时批量读取填充缓冲区,readLine()在BufferedReader中按行切分缓存内容)。
多层装饰的组装方式与实际意义
缓冲流可与其他装饰器任意嵌套,顺序决定行为逻辑链。常见组合有:
-
字节流缓冲 + 解压缩:
new GZIPInputStream(new BufferedInputStream(new FileInputStream("data.gz")))先缓冲再解压,避免每次解压都触发磁盘 I/O; -
字符流缓冲 + 编码转换:
new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"), "UTF-8"))底层是字节流,中间转码,顶层缓冲并支持readLine(); -
缓冲 + 类型解析:
new DataInputStream(new BufferedInputStream(new FileInputStream("data.bin")))缓冲提升效率,DataInputStream提供readInt()、readDouble()等二进制语义解析能力。
这种组合不是硬编码在类名里,而是在创建对象那一刻由开发者决定,运行时才形成完整责任链。
立即学习“Java免费学习笔记(深入)”;
使用缓冲流时的关键注意事项
灵活的前提是理解其行为边界和协作规则:
-
关闭外层即关闭全部:调用
bufferedReader.close()会逐层调用close(),最终关闭底层FileInputStream。无需、也不应手动关闭内层流; -
缓冲区大小可定制但通常无需干预:
BufferedInputStream(InputStream in, int size)允许指定缓冲容量,默认 8192 字节已适配多数场景; -
flush() 对输入流无意义,对输出流必须显式调用:
BufferedOutputStream需flush()确保缓冲内容落盘,而输入类的flush()是空实现; -
异常传播是透明的:任一装饰层抛出
IOException,上层调用者只需捕获一次,无需关心哪一层触发。
为什么不用继承方式实现缓冲?
如果为每个节点流都写一个带缓冲的子类(比如 BufferedFileInputStream、BufferedSocketInputStream),会带来爆炸式类膨胀:
- InputStream 有十几种子类,每种再加缓冲、再加解压、再加序列化……组合数迅速失控;
- 新增功能(如日志记录、加密、限速)都要重复继承,违反开闭原则;
- 缓冲逻辑被复制多份,难以统一维护和优化。
装饰器让“缓冲”变成一个可插拔的能力模块,一套代码服务所有流类型,这才是真正意义上的灵活。


















