FilterOutputStream
是 Java I/O 体系中一个关键的抽象类,位于 java.io
包下。它的核心作用是为所有「过滤输出流」提供基础实现,通过装饰器模式(Decorator Pattern)动态扩展输出流的功能。本文将从源码结构、设计模式、核心方法实现等角度,深入解析这个类的设计思想。
public class FilterOutputStream extends OutputStream {
protected OutputStream out; // 被装饰的底层输出流
public FilterOutputStream(OutputStream out) { this.out = out; }
}
FilterOutputStream
直接继承自 OutputStream
(所有字节输出流的抽象基类),并通过组合(protected OutputStream out
)持有一个底层输出流实例。这种「继承+组合」的设计是装饰器模式的典型特征。
装饰器模式的核心是在不修改原有对象的前提下,动态扩展其功能。它通过将原始对象包装在装饰器类中,允许通过组合不同的装饰器子类,灵活叠加多种功能(如缓冲、加密、数据类型转换等)。
FilterOutputStream
如何体现装饰器模式?FilterOutputStream
继承自 OutputStream
,与原始流(如 FileOutputStream
)具有相同的接口(write
、flush
、close
等方法),保证装饰器与被装饰对象可以互相替换。write
、flush
)默认直接调用底层流(out
)的对应方法,将基础功能委托给被装饰对象。BufferedOutputStream
、DataOutputStream
)通过重写方法,在委托基础功能的同时添加额外逻辑(如缓冲、类型转换),实现功能扩展。write(int b)
:基础写操作public void write(int b) throws IOException {
out.write(b); // 直接委托给底层流
}
该方法将单个字节的写操作直接转发给底层输出流 out
。这是最基础的写操作,子类可以重写此方法添加过滤逻辑(例如 CipherOutputStream
会在此方法中加密字节)。
write(byte[] b)
:批量写操作public void write(byte b[]) throws IOException {
write(b, 0, b.length); // 调用带偏移量的重载方法
}
该方法将整个字节数组的写操作委托给 write(byte[] b, int off, int len)
,保持逻辑统一。
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 次数。
flush()
:刷新缓冲区public void flush() throws IOException {
out.flush(); // 委托给底层流刷新
}
刷新操作同样委托给底层流。如果子类有自定义缓冲区(如 BufferedOutputStream
),会先将缓冲区数据写入底层流,再调用 out.flush()
。
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 流的核心机制,以及如何通过设计模式解决实际开发中的扩展问题。