> 技术文档 > 【Java 源码阅读系列31】深度解读Java FilterOutputStream 源码

【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)具有相同的接口(writeflushclose 等方法),保证装饰器与被装饰对象可以互相替换。
  • 功能委托:所有核心方法(如 writeflush)默认直接调用底层流(out)的对应方法,将基础功能委托给被装饰对象。
  • 扩展灵活性:子类(如 BufferedOutputStreamDataOutputStream)通过重写方法,在委托基础功能的同时添加额外逻辑(如缓冲、类型转换),实现功能扩展。

三、核心方法源码解析

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 语法自动关闭底层流(ostreamout 的引用),并在关闭前调用 flush() 确保数据全部写入。此设计避免了手动关闭流可能导致的资源泄漏。


四、子类扩展示例:以 BufferedOutputStream 为例

BufferedOutputStreamFilterOutputStream 的典型子类,通过添加缓冲区优化写操作效率:

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 次数,提升了性能。


五、设计思想总结

  1. 装饰器模式的优势
    FilterOutputStream 通过组合而非继承扩展功能,避免了继承链膨胀(例如无需为「缓冲+加密」「缓冲+数据类型转换」等组合创建大量子类),符合「开闭原则」(对扩展开放,对修改关闭)。

  2. 单一职责原则
    FilterOutputStream 仅负责定义装饰器的基础结构,具体功能扩展由子类实现(如 BufferedOutputStream 处理缓冲,DataOutputStream 处理基本数据类型),每个类专注于单一功能。

  3. 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 流的核心机制,以及如何通过设计模式解决实际开发中的扩展问题。