【Java 源码阅读系列32】深度解读Java FilterWriter 源码

FilterWriter 是 Java I/O 体系中字符输出流的核心抽象类,位于 java.io 包下。它与字节流体系中的 FilterOutputStream 形成对称设计,共同构成 Java I/O「装饰器模式」的基础框架。本文将结合源码,从类结构、设计模式、核心方法实现等角度,深入解析 FilterWriter 的设计思想。

一、类定义与核心结构

1. 类继承关系与核心成员

public abstract class FilterWriter extends Writer {
    protected Writer out; // 被装饰的底层字符输出流

    protected FilterWriter(Writer out) {
        super(out); // 调用父类 Writer 的构造方法(传递锁对象)
        this.out = out;
    }
}
  • 继承链FilterWriter 继承自抽象类 Writer(所有字符输出流的基类),强制要求子类实现字符输出的核心能力。
  • 组合设计:通过 protected Writer out 持有一个底层字符输出流实例(如 FileWriterStringWriter),这是装饰器模式的核心特征——通过组合而非继承扩展功能。
  • 抽象性:类被声明为 abstract,无法直接实例化,必须由子类(如 BufferedWriterOutputStreamWriter)提供具体实现。

二、设计模式:装饰器模式的字符流实践

1. 装饰器模式的核心逻辑

装饰器模式的核心是在不修改原始对象的前提下,动态叠加功能。其关键要素包括:

  • 接口一致性:装饰器与被装饰对象实现相同接口(或继承相同抽象类),保证行为一致;
  • 功能委托:装饰器默认将请求转发给被装饰对象,子类通过重写方法添加额外逻辑;
  • 灵活组合:通过嵌套装饰器,实现多种功能的自由组合(如「缓冲+编码转换」)。

2. FilterWriter 如何体现装饰器模式?

  • 接口一致性FilterWriter 继承自 Writer,与所有字符输出流(如 FileWriter)共享 writeflushclose 等核心方法,确保装饰器与原始流可以无缝替换。
  • 基础功能委托:所有方法(如 write(int c)flush())默认直接调用底层流 out 的对应方法(如 out.write(c)),将基础字符输出功能委托给被装饰对象。
  • 扩展开放性:子类通过重写方法实现功能扩展(例如 BufferedWriter重写 write 方法添加缓冲逻辑),符合「开闭原则」(对扩展开放,对修改关闭)。

三、核心方法源码解析

FilterWriter 的方法设计高度统一,核心逻辑均是将请求转发给底层流 out,以下是关键方法的详细分析:

1. write(int c):写入单个字符

public void write(int c) throws IOException {
    out.write(c); // 直接委托给底层流
}

该方法将单个字符的写入操作直接转发给底层流 out。子类可重写此方法添加过滤逻辑(例如 CipherWriter 可在此加密字符)。

2. write(char[] cbuf, int off, int len):写入字符数组的一部分

public void write(char cbuf[], int off, int len) throws IOException {
    out.write(cbuf, off, len); // 委托给底层流的同名方法
}

此方法将字符数组的指定区间写入操作委托给底层流。默认实现不做任何额外处理,子类(如 BufferedWriter)会重写此方法,先将数据写入缓冲区,待缓冲区满时再批量写入底层流,减少 I/O 次数。

3. write(String str, int off, int len):写入字符串的一部分

public void write(String str, int off, int len) throws IOException {
    out.write(str, off, len); // 委托给底层流处理字符串
}

字符串是字符的高级抽象,此方法将字符串的区间写入操作直接转发给底层流。例如 OutputStreamWriterFilterWriter 的子类)会在此方法中完成字符到字节的编码转换。

4. flush():刷新缓冲区

public void flush() throws IOException {
    out.flush(); // 委托底层流刷新
}

刷新操作的本质是将缓冲区中未写入的字符强制输出到目标设备。FilterWriter 的默认实现直接调用底层流的 flush,子类(如 BufferedWriter)会先将自定义缓冲区的数据写入底层流,再触发底层流的刷新。

5. close():关闭流

public void close() throws IOException {
    out.close(); // 直接关闭底层流
}

关闭流时,FilterWriter 直接调用底层流的 close 方法释放资源。由于 Writer 实现了 AutoCloseable 接口,可通过 try-with-resources 语法自动关闭。


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

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

public class BufferedWriter extends FilterWriter {
    private char[] cb; // 字符缓冲区
    private int nChars; // 缓冲区容量
    private int nextChar; // 下一个写入位置

    public BufferedWriter(Writer out, int sz) {
        super(out);
        if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0");
        this.cb = new char[sz];
        this.nChars = sz;
    }

    public void write(char[] cbuf, int off, int len) throws IOException {
        if (len == 0) return;
        // 如果数据量超过缓冲区大小,直接刷新缓冲区并写入底层流
        if (len >= nChars) {
            flushBuffer();
            out.write(cbuf, off, len);
            return;
        }
        // 数据量较小:写入缓冲区
        int b = off;
        int t = off + len;
        while (b < t) {
            int d = nextChar + (b - off);
            if (d >= nChars) { // 缓冲区剩余空间不足,先刷新
                flushBuffer();
                d = b - off;
            }
            int l = Math.min(t - b, nChars - d);
            System.arraycopy(cbuf, b, cb, d, l);
            b += l;
            nextChar += l;
        }
    }

    private void flushBuffer() throws IOException {
        if (nextChar > 0) {
            out.write(cb, 0, nextChar); // 批量写入底层流
            nextChar = 0; // 重置缓冲区指针
        }
    }
}

通过重写 write 方法,BufferedWriter优先将字符写入内部缓冲区,仅当缓冲区满或数据量过大时,才调用底层流的 write 方法。这种批量写入机制显著减少了 I/O 操作次数,提升了字符输出效率。


五、设计思想总结

  1. 装饰器模式的普适性
    FilterWriterFilterOutputStream 共同构成 Java I/O 的装饰器基础框架,分别处理字符流与字节流的功能扩展。这种设计使得开发者可以通过嵌套装饰器(如 new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"))))灵活组合「缓冲+编码转换+文件输出」等功能,避免了继承链膨胀。

  2. 抽象类的职责分离
    FilterWriter 作为抽象装饰器,仅定义基础行为(转发请求),将具体功能扩展(如缓冲、编码)交给子类实现。这种「抽象基类定义框架,子类实现细节」的设计,符合「单一职责原则」。

  3. I/O 流的可组合性
    由于所有过滤字符流都继承自 FilterWriter,开发者可以根据需求动态叠加功能。例如:

    Writer writer = new FileWriter("data.txt");       // 基础文件写入流
    Writer bufferedWriter = new BufferedWriter(writer); // 缓冲功能
    Writer utf8Writer = new OutputStreamWriter(         // 编码转换功能(字节转字符)
        new FileOutputStream("data.txt"), 
        StandardCharsets.UTF_8
    );
    

六、总结

FilterWriter 是 Java 字符输出流体系中装饰器模式的核心实现,通过「继承+组合」的方式,为字符流的功能扩展提供了灵活的基础框架。理解其设计思想,不仅能深入掌握 Java I/O 流的运行机制,更能学会如何通过设计模式解决实际开发中「动态功能扩展」的问题。

你可能感兴趣的:(源码阅读系列之Java,java,开发语言)