Java自定义流包装器核心是采用装饰器模式组合而非继承,字节流基于InputStream/OutputStream、字符流基于Reader/Writer,二者通过InputStreamReader/OutputStreamWriter桥接;如CountingInputStream统计读取字节数,UppercaseWriter实现自动大写输出。

Java 中自定义流包装器的核心是继承或组合已有流类,并在读写过程中添加自定义逻辑(如日志、加密、缓冲、编码转换等)。字节流基于 InputStream/OutputStream,字符流基于 Reader/Writer;二者不能混用,但可通过 InputStreamReader/OutputStreamWriter 桥接。
字节流包装器:继承 InputStream/OutputStream
推荐使用“装饰器模式”——不直接继承,而是持有一个被包装的流对象,在构造时传入,再重写关键方法(如 read()、write()、close())来增强行为。
例如,实现一个带计数功能的输入流包装器:
public class CountingInputStream extends InputStream {
private final InputStream in;
private long bytesRead = 0;
public CountingInputStream(InputStream in) {
this.in = in;
}
@Override
public int read() throws IOException {
int b = in.read();
if (b != -1) bytesRead++;
return b;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int n = in.read(b, off, len);
if (n > 0) bytesRead += n;
return n;
}
@Override
public void close() throws IOException {
in.close();
}
public long getBytesRead() {
return bytesRead;
}
}使用方式:
try (CountingInputStream cis = new CountingInputStream(new FileInputStream("data.bin"))) {
cis.readAllBytes();
System.out.println("已读字节数:" + cis.getBytesRead());
}字符流包装器:继承 Reader/Writer
同理,字符流包装器应封装 Reader 或 Writer,并在读写字符/字符数组时插入逻辑。注意:字符流操作的是 Unicode 字符,内部会通过指定字符集将字节转为字符(由底层字节流提供)。
例如,实现一个自动大写输出的 Writer 包装器:
public class UppercaseWriter extends Writer {
private final Writer out;
public UppercaseWriter(Writer out) {
this.out = out;
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
char[] upper = new char[len];
for (int i = 0; i 使用方式:
try (UppercaseWriter uw = new UppercaseWriter(new FileWriter("out.txt"))) {
uw.write("hello world");
}关键注意事项
-
务必委托 close():包装器的
close()必须调用被包装流的close(),否则资源泄漏 -
异常要透传:不要吞掉
IOException,除非你明确要处理并重新抛出 - 避免重复关闭:若包装器被多次关闭,需确保被包装流只关一次(可加标志位)
-
字符流不可直接包装字节流:必须经
InputStreamReader或OutputStreamWriter转换,且需指定正确字符集 -
缓冲建议复用现有类:如需缓冲,优先组合
BufferedInputStream或BufferedWriter,而非自己实现缓冲区
进阶:统一接口抽象(可选)
如果需要同时支持字节和字符场景,可定义泛型包装器接口,或使用适配器桥接。例如,把 CountingInputStream 的统计能力也扩展到字符流(统计字符数而非字节数),只需另写一个 CountingReader,逻辑类似但操作 char[] 和 read() 返回值。
不建议强行统一字节/字符抽象——JDK 的设计本意就是分离二者,混淆会导致编码错误或乱码。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











