FilterReader
是 Java I/O 体系中字符输入流的核心抽象类,位于 java.io
包下。它与 FilterWriter
(字符输出流)、FilterOutputStream
(字节输出流)共同构成 Java I/O 的「装饰器模式」基础框架,旨在通过动态组合扩展字符输入流的功能。本文将结合源码,从类结构、设计模式、核心方法实现等角度,深入解析 FilterReader
的设计思想。
public abstract class FilterReader extends Reader {
protected Reader in; // 被装饰的底层字符输入流
protected FilterReader(Reader in) {
super(in); // 调用父类 Reader 的构造方法(传递锁对象)
this.in = in;
}
}
FilterReader
继承自抽象类 Reader
(所有字符输入流的基类),强制子类实现字符读取的核心能力。protected Reader in
持有一个底层字符输入流实例(如 FileReader
、StringReader
),这是装饰器模式的核心特征——通过组合而非继承扩展功能。abstract
,无法直接实例化,必须由子类(如 BufferedReader
、PushbackReader
)提供具体实现。装饰器模式的核心是在不修改原始对象的前提下,动态叠加功能。其关键要素包括:
FilterReader
如何体现装饰器模式?FilterReader
继承自 Reader
,与所有字符输入流(如 FileReader
)共享 read
、skip
、mark
等核心方法,确保装饰器与原始流可以无缝替换。read()
、skip(long n)
)默认直接调用底层流 in 的对应方法(如 in.read()
),将基础字符读取功能委托给被装饰对象。BufferedReader
重写 read
方法添加缓冲逻辑,PushbackReader
重写 read
实现字符回退),符合「开闭原则」(对扩展开放,对修改关闭)。FilterReader
的方法设计高度统一,核心逻辑均是将请求转发给底层流 in
,以下是关键方法的详细分析:
read()
:读取单个字符public int read() throws IOException {
return in.read(); // 直接委托给底层流读取单个字符
}
该方法将单个字符的读取操作直接转发给底层流 in
。返回值为读取的字符(int
类型,范围 0-65535
),若到达流末尾则返回 -1
。子类可重写此方法添加过滤逻辑(例如 CipherReader
可在此解密字符)。
read(char[] cbuf, int off, int len)
:读取字符到数组public int read(char cbuf[], int off, int len) throws IOException {
return in.read(cbuf, off, len); // 委托给底层流读取指定区间的字符
}
此方法将字符读取到数组的指定区间,返回实际读取的字符数(若到达流末尾则返回 -1
)。默认实现不做任何额外处理,子类(如 BufferedReader
)会重写此方法,先从内部缓冲区读取字符,缓冲区不足时再从底层流批量加载数据,减少 I/O 次数。
skip(long n)
:跳过指定数量的字符public long skip(long n) throws IOException {
return in.skip(n); // 委托底层流跳过字符
}
跳过 n
个字符后,后续读取操作将从跳过的位置继续。底层流(如 FileReader
)的 skip
方法通常通过移动文件指针实现,而装饰器子类(如 BufferedReader
)可能通过调整缓冲区指针优化此操作。
ready()
:判断流是否可立即读取public boolean ready() throws IOException {
return in.ready(); // 委托底层流判断是否就绪
}
返回 true
表示流已准备好立即读取(例如缓冲区有数据或底层流支持非阻塞读取)。子类可重写此方法(如 BufferedReader
若缓冲区有数据则直接返回 true
,无需等待底层流)。
mark(int readAheadLimit)
与 reset()
:标记与重置public void mark(int readAheadLimit) throws IOException {
in.mark(readAheadLimit); // 委托底层流标记位置
}
public void reset() throws IOException {
in.reset(); // 委托底层流重置到标记位置
}
mark
方法在流中标记一个位置,reset
方法将流重置到该位置(仅当流支持标记时有效)。子类(如 BufferedReader
)可扩展此功能,例如在缓冲区中保存标记位置,避免直接调用底层流的 mark
(可能不支持)。
BufferedReader
为例BufferedReader
是 FilterReader
的典型子类(实际 JDK 中 BufferedReader
直接继承 Reader
,但设计思想一致),通过添加字符缓冲区优化读取效率:
public class BufferedReader extends Reader {
private char[] cb; // 字符缓冲区
private int nChars; // 缓冲区总容量
private int nextChar; // 缓冲区当前读取位置
private Reader in; // 底层字符输入流
public int read(char[] cbuf, int off, int len) throws IOException {
if (len == 0) return 0;
// 优先从缓冲区读取
if (nextChar >= nChars) {
fill(); // 缓冲区空,从底层流加载数据到缓冲区
if (nextChar >= nChars) return -1; // 底层流无数据
}
// 计算可读取的字符数(缓冲区剩余量与请求量的较小值)
int avail = nChars - nextChar;
int cnt = (avail < len) ? avail : len;
System.arraycopy(cb, nextChar, cbuf, off, cnt);
nextChar += cnt;
return cnt;
}
private void fill() throws IOException {
// 从底层流读取数据到缓冲区
int n = in.read(cb, 0, cb.length);
if (n > 0) {
nChars = n;
nextChar = 0;
}
}
}
通过重写 read
方法,BufferedReader
优先从缓冲区读取字符,仅当缓冲区无数据时才调用底层流的 read
方法批量加载数据。这种「预加载-缓冲读取」机制显著减少了 I/O 操作次数,提升了字符输入效率。
装饰器模式的普适性:
FilterReader
与 FilterWriter
、FilterOutputStream
共同构成 Java I/O 的装饰器基础框架,分别处理字符输入、字符输出、字节输出的功能扩展。这种设计使得开发者可以通过嵌套装饰器(如 new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")))
)灵活组合「缓冲+编码转换+文件读取」等功能,避免了继承链膨胀。
抽象类的职责分离:
FilterReader
作为抽象装饰器,仅定义基础行为(转发请求),将具体功能扩展(如缓冲、回退、字符转换)交给子类实现。这种「抽象基类定义框架,子类实现细节」的设计,符合「单一职责原则」。
I/O 流的可组合性:
由于所有过滤字符输入流都基于 FilterReader
或其设计思想,开发者可以根据需求动态叠加功能。例如:
Reader reader = new FileReader("data.txt"); // 基础文件读取流
Reader bufferedReader = new BufferedReader(reader); // 缓冲功能(提升读取效率)
Reader pushbackReader = new PushbackReader(bufferedReader); // 回退功能(允许读取后撤销)
FilterReader
是 Java 字符输入流体系中装饰器模式的核心实现,通过「继承+组合」的方式,为字符流的功能扩展提供了灵活的基础框架。理解其设计思想,不仅能深入掌握 Java I/O 流的运行机制,更能学会如何通过设计模式解决实际开发中「动态功能扩展」的问题。