【Java 源码阅读系列31】深度解读Java FilterOutputStream 源码
FilterOutputStream
是 Java I/O 体系中一个关键的抽象类,位于 java.io
包下。它的核心作用是为所有「过滤输出流」提供基础实现,通过装饰器模式(Decorator Pattern)动态扩展输出流的功能。本文将从源码结构、设计模式、核心方法实现等角度,深入解析这个类的设计思想。
一、类定义与核心结构
1. 类继承关系
public class FilterOutputStream extends OutputStream { protected OutputStream out; // 被装饰的底层输出流 public FilterOutputStream(OutputStream out) { this.out = out; }}
FilterOutputStream
直接继承自 OutputStream
(所有字节输出流的抽象基类),并通过组合(protected OutputStream out
)持有一个底层输出流实例。这种「继承+组合」的设计是装饰器模式的典型特征。
二、设计模式:装饰器模式的实践
1. 装饰器模式的核心目标
装饰器模式的核心是在不修改原有对象的前提下,动态扩展其功能。它通过将原始对象包装在装饰器类中,允许通过组合不同的装饰器子类,灵活叠加多种功能(如缓冲、加密、数据类型转换等)。
2. FilterOutputStream
如何体现装饰器模式?
- 接口一致性:
FilterOutputStream
继承自OutputStream
,与原始流(如FileOutputStream
)具有相同的接口(write
、flush
、close
等方法),保证装饰器与被装饰对象可以互相替换。 - 功能委托:所有核心方法(如
write
、flush
)默认直接调用底层流(out
)的对应方法,将基础功能委托给被装饰对象。 - 扩展灵活性:子类(如
BufferedOutputStream
、DataOutputStream
)通过重写方法,在委托基础功能的同时添加额外逻辑(如缓冲、类型转换),实现功能扩展。
三、核心方法源码解析
1. write(int b)
:基础写操作
public void write(int b) throws IOException { out.write(b); // 直接委托给底层流}
该方法将单个字节的写操作直接转发给底层输出流 out
。这是最基础的写操作,子类可以重写此方法添加过滤逻辑(例如 CipherOutputStream
会在此方法中加密字节)。
2. write(byte[] b)
:批量写操作
public void write(byte b[]) throws IOException { write(b, 0, b.length); // 调用带偏移量的重载方法}
该方法将整个字节数组的写操作委托给 write(byte[] b, int off, int len)
,保持逻辑统一。
3. write(byte[] b, int off, int len)
:带偏移量的批量写
public void write(byte b[], int off, int len) throws IOException { if ((off | len | (b.length - (len + off)) | (off + len)) < 0) throw new IndexOutOfBoundsException(); for (int i = 0 ; i < len ; i++) { write(b[off + i]); // 逐个字节调用 write(int b) }}
此方法的默认实现效率较低(通过循环逐个字节写入),因此注释中明确提示:「子类应提供更高效的实现」。例如 BufferedOutputStream
会重写此方法,先将数据写入内部缓冲区,待缓冲区满时再批量写入底层流,减少 I/O 次数。
4. flush()
:刷新缓冲区
public void flush() throws IOException { out.flush(); // 委托给底层流刷新}
刷新操作同样委托给底层流。如果子类有自定义缓冲区(如 BufferedOutputStream
),会先将缓冲区数据写入底层流,再调用 out.flush()
。
5. close()
:关闭流
@SuppressWarnings(\"try\")public void close() throws IOException { try (OutputStream ostream = out) { flush(); // 关闭前强制刷新 }}
通过 try-with-resources
语法自动关闭底层流(ostream
是 out
的引用),并在关闭前调用 flush()
确保数据全部写入。此设计避免了手动关闭流可能导致的资源泄漏。
四、子类扩展示例:以 BufferedOutputStream
为例
BufferedOutputStream
是 FilterOutputStream
的典型子类,通过添加缓冲区优化写操作效率:
public class BufferedOutputStream extends FilterOutputStream { private byte[] buf; // 自定义缓冲区 private int count; // 缓冲区当前数据量 public void write(byte[] b, int off, int len) throws IOException { if (len >= buf.length) { // 数据量超过缓冲区大小 flushBuffer(); // 先刷新缓冲区 out.write(b, off, len); // 直接写入底层流 return; } // 数据量较小:写入缓冲区 if (len > buf.length - count) { flushBuffer(); // 缓冲区不足,先刷新 } System.arraycopy(b, off, buf, count, len); count += len; } private void flushBuffer() throws IOException { if (count > 0) { out.write(buf, 0, count); // 批量写入底层流 count = 0; } }}
通过重写 write
方法,BufferedOutputStream
在数据写入时优先使用缓冲区,仅当缓冲区满或数据量过大时才调用底层流的 write
,显著减少了 I/O 次数,提升了性能。
五、设计思想总结
-
装饰器模式的优势:
FilterOutputStream
通过组合而非继承扩展功能,避免了继承链膨胀(例如无需为「缓冲+加密」「缓冲+数据类型转换」等组合创建大量子类),符合「开闭原则」(对扩展开放,对修改关闭)。 -
单一职责原则:
FilterOutputStream
仅负责定义装饰器的基础结构,具体功能扩展由子类实现(如BufferedOutputStream
处理缓冲,DataOutputStream
处理基本数据类型),每个类专注于单一功能。 -
I/O 流的可组合性:
由于所有过滤流都继承自FilterOutputStream
,开发者可以通过嵌套装饰器灵活组合功能。例如:OutputStream os = new FileOutputStream(\"data.txt\");OutputStream bufferedOs = new BufferedOutputStream(os); // 缓冲功能OutputStream dataOs = new DataOutputStream(bufferedOs); // 数据类型转换功能dataOs.writeInt(123); // 写入 int 类型数据(先经 DataOutputStream 转换,再经 BufferedOutputStream 缓冲,最后写入文件)
六、总结
FilterOutputStream
是 Java I/O 体系中装饰器模式的经典实现,它通过「继承+组合」的方式,为输出流的功能扩展提供了灵活的基础框架。理解其设计思想,有助于掌握 Java I/O 流的核心机制,以及如何通过设计模式解决实际开发中的扩展问题。