FilterReader.class源码——字符流的装饰器基类

发布时间:2026/7/7 6:07:50
FilterReader.class源码——字符流的装饰器基类 FilterReader 的UML关系图如下所示FilterReader.class的源码如下所示package java.io; /** * Abstract class for reading filtered character streams. * The abstract class codeFilterReader/code itself * provides default methods that pass all requests to * the contained stream. Subclasses of codeFilterReader/code * should override some of these methods and may also provide * additional methods and fields. * * author Mark Reinhold * since JDK1.1 */ public abstract class FilterReader extends Reader { //实际被装饰的字符输入流 protected Reader in; //当用构造函数创建这个装饰器时传入一个被装饰者的字符输入流 protected FilterReader(Reader in) { super(in); this.in in; } //调用实际被装饰的字符输入流的read() 函数 public int read() throws IOException { return in.read(); } //调用实际被装饰的字符输入流的read(char cbuf[], int off, int len) 函数 public int read(char cbuf[], int off, int len) throws IOException { return in.read(cbuf, off, len); } //调用实际被装饰的字符输入流的skip(long n) 函数 public long skip(long n) throws IOException { return in.skip(n); } //调用实际被装饰的字符输入流的ready() 函数 public boolean ready() throws IOException { return in.ready(); } //调用实际被装饰的字符输入流的markSupported() 函数 public boolean markSupported() { return in.markSupported(); } //调用实际被装饰的字符输入流的mark(int readAheadLimit) 函数 public void mark(int readAheadLimit) throws IOException { in.mark(readAheadLimit); } //调用实际被装饰的字符输入流的reset() 函数 public void reset() throws IOException { in.reset(); } //调用实际被装饰的字符输入流的close() 函数 public void close() throws IOException { in.close(); } }三、PushbackReader.class源码——可以回退字符到被读取的字符流中的字符流装饰器类PushbackReader与PushbackInputStream类似只不过PushbackReader提供了有限字符内部定义了一个默认长度为1的char[] buf字符数组的缓冲式回退能力具体过程如下①、当调用unread()函数时会将任意字符可以是从被装饰的输入流中读取的字节也可以是自己定义的字符压入char[] buf字符数组的尾部pos-1的位置②、当后续调用read()函数时优先读取步骤①中这个char[] buf字符数组中被压入的字符。PushbackInputStream.class的相关源码和使用方式请参照13、PushbackInputStream和StreamTokenizer的源码分析和使用方法详细分析3.1、PushbackReader.class的源码分析PushbackReader.class 的UML关系图如下所示PushbackReader.class 的源码如下所示package java.io; public class PushbackReader extends FilterReader { //有限长度的用于回退的字符数组缓冲区默认长度为1 private char[] buf; //可读指针char[] buf有限长度的用于回退的字符数组缓冲区中该指针包括该指针索引之后的所有字符都可以读 private int pos; //构造函数in为被装饰的字符输入流size为char[] buf有限长度的用于回退的字符数组缓冲区的长度 public PushbackReader(Reader in, int size) { super(in); if (size 0) { throw new IllegalArgumentException(size 0); } this.buf new char[size]; this.pos size;//将可读指针指向char[] buf有限长度的用于回退的字符数组缓冲区中最后一个索引size-1之后 } //构造函数in为被装饰的字符输入流 public PushbackReader(Reader in) { this(in, 1);//构造一个默认长度为1的char[] buf用于回退的字符数组缓冲区 } //检查被装饰的字符输入流是否关闭 private void ensureOpen() throws IOException { if (buf null) throw new IOException(Stream closed); } //这个函数在多线程的场景下运行时是线程安全的 //如果char[] buf用于回退的字符数组缓冲区中有可读的字符的话就从该缓冲区中读取1个字符 //如果char[] buf用于回退的字符数组缓冲区中没有可读的字符的话就从被装饰的输入流中读取1个字符 //如果char[] buf用于回退的字符数组缓冲区和被装饰的输入流中都没有可读的字符的话返回-1 public int read() throws IOException { synchronized (lock) {//用Reader.class::lock变量指向的对象锁来同步线程 ensureOpen(); if (pos buf.length) return buf[pos]; else return super.read(); } } //这个函数在多线程的场景下运行时是线程安全的尽可能的从char[] buf用于回退的字符数组缓冲区和被装饰的输入流中读取len个字符到char[] cbuf的[off,offlen)索引位置总共分为以下5种场景 //①、如果char[] buf用于回退的字符数组缓冲区中有len个字符的话就从该缓冲区中读取len个字符到字符数组char[] cbuf的[off,offlen)索引位置 //②、如果char[] buf用于回退的字符数组缓冲区中没有任何字符并且被装饰的字符输入流中有len个字符那就从被装饰的字符输入流中读取len个字符到字符数组char[] cbuf的[off,offlen)索引位置 //③、如果char[] buf用于回退的字符数组缓冲区中没有任何字符并且被装饰的字符输入流中只有availavaillen个字符那就从被装饰的字符输入流中读取avail个字符到字符数组char[] cbuf的[off,offavail)索引位置 //④、如果char[] buf用于回退的字符数组缓冲区中有availavaillen个字符的话就读取avail个字符剩余len-avail个字符从被装饰的字符输入流中读取如果被装饰的字符输入流中没有len-avail个字符的话那就从被装饰的输入流中有多少读取多少直到将被装饰的输入流读取完毕然后将以上2个地方被装饰的输入流用于回退的字符数组缓冲区读取的所有字符假如有x个放入到字符数组char[] cbuf的[off,offx)索引位置 //⑤、如果char[] buf用于回退的字符数组缓冲区和被装饰的字符输入流中都没有任何字符的话返回-1 public int read(char cbuf[], int off, int len) throws IOException { synchronized (lock) { //检查被装饰的字符输入流是否关闭 ensureOpen(); try { if (len 0) { if (len 0) { throw new IndexOutOfBoundsException(); } else if ((off 0) || (off cbuf.length)) {//相当于off len cbuf.length源码中这样写代码的好处我没看出来 throw new IndexOutOfBoundsException(); } return 0;//要从PushbackReader 对象中读取的len个字符0时返回0 } int avail buf.length - pos;//用于回退的字符数组缓冲区中实际装载了buf.length - pos个字符 if (avail 0) { if (len avail) avail len; System.arraycopy(buf, pos, cbuf, off, avail); pos avail; off avail; len - avail; } if (len 0) { len super.read(cbuf, off, len); if (len -1) { return (avail 0) ? -1 : avail; } return avail len;//场景④中的x就是这里的avail len } return avail; } catch (ArrayIndexOutOfBoundsException e) { throw new IndexOutOfBoundsException(); } } } //一次只可以回推1个字符数据到char[] buf用于回退的字符数组缓冲区中 public void unread(int c) throws IOException { synchronized (lock) { ensureOpen(); if (pos 0)//pos0时表示char[] buf用于回退的字符数组缓冲区中已经没有足够的容量再放置数据所以抛出一个IOException异常。 throw new IOException(Pushback buffer overflow); buf[--pos] (char) c; } } //一次回推char[] cbuf字符数组中[off,offlen)索引位置的len个字符数据到char[] buf用于回退的字符数组缓冲区中 public void unread(char cbuf[], int off, int len) throws IOException { synchronized (lock) { ensureOpen(); if (len pos)//如果char[] buf用于回退的字符数组缓冲区中没有足够的位置放置len个字符则抛出一个IOException throw new IOException(Pushback buffer overflow); pos - len;//如果char[] buf用于回退的字符数组缓冲区中有足够的位置放置len个字符则使用System.arraycopy()函数进行回退 System.arraycopy(cbuf, off, buf, pos, len); } } public void unread(char cbuf[]) throws IOException { unread(cbuf, 0, cbuf.length); } public boolean ready() throws IOException { synchronized (lock) { ensureOpen(); return (pos buf.length) || super.ready(); } } //不支持mark()函数 public void mark(int readAheadLimit) throws IOException { throw new IOException(mark/reset not supported); } //不支持reset()函数 public void reset() throws IOException { throw new IOException(mark/reset not supported); } //不支持mark()函数 public boolean markSupported() { return false; } //关闭被装饰的字符输入流和用于回退的字符数组缓冲区 public void close() throws IOException { super.close(); buf null; } //从char[] buf用于回退的字符数组缓冲区被装饰的字符输入流中跳过n个字符如果char[] buf用于回退的字符数组缓冲区被装饰的字符输入流中的字符数量n则返回实际跳过的字符数量 public long skip(long n) throws IOException { if (n 0L) throw new IllegalArgumentException(skip value is negative); synchronized (lock) { ensureOpen(); int avail buf.length - pos;//从char[] buf用于回退的字符数组缓冲区中跳过的字符 if (avail 0) { if (n avail) { pos n; return n; } else { pos buf.length; n - avail; } } return avail super.skip(n);//从被装饰的字符输入流中跳过的字符累加到从char[] buf用于回退的字符数组缓冲区中跳过的字符 } } }3.2、PushbackReader.class的read()函数和unread()函数package java.io; public class PushbackReader extends FilterReader { //有限长度的用于回退的字符数组缓冲区默认长度为1 private char[] buf; //可读指针char[] buf有限长度的用于回退的字符数组缓冲区中该指针包括该指针索引之后的所有字符都可以读 private int pos; ...省略部分代码... //这个函数在多线程的场景下运行时是线程安全的尽可能的从char[] buf用于回退的字符数组缓冲区和被装饰的输入流中读取len个字符到char[] cbuf的[off,offlen)索引位置总共分为以下5种场景 //①、如果char[] buf用于回退的字符数组缓冲区中有len个字符的话就从该缓冲区中读取len个字符到字符数组char[] cbuf的[off,offlen)索引位置 //②、如果char[] buf用于回退的字符数组缓冲区中没有任何字符并且被装饰的字符输入流中有len个字符那就从被装饰的字符输入流中读取len个字符到字符数组char[] cbuf的[off,offlen)索引位置 //③、如果char[] buf用于回退的字符数组缓冲区中没有任何字符并且被装饰的字符输入流中只有availavaillen个字符那就从被装饰的字符输入流中读取avail个字符到字符数组char[] cbuf的[off,offavail)索引位置 //④、如果char[] buf用于回退的字符数组缓冲区中有availavaillen个字符的话就读取avail个字符剩余len-avail个字符从被装饰的字符输入流中读取如果被装饰的字符输入流中没有len-avail个字符的话那就从被装饰的输入流中有多少读取多少直到将被装饰的输入流读取完毕然后将以上2个地方被装饰的输入流用于回退的字符数组缓冲区读取的所有字符假如有x个放入到字符数组char[] cbuf的[off,offx)索引位置 //⑤、如果char[] buf用于回退的字符数组缓冲区和被装饰的字符输入流中都没有任何字符的话返回-1 public int read(char cbuf[], int off, int len) throws IOException { synchronized (lock) { //检查被装饰的字符输入流是否关闭 ensureOpen(); try { if (len 0) { if (len 0) { throw new IndexOutOfBoundsException(); } else if ((off 0) || (off cbuf.length)) {//相当于off len cbuf.length源码中这样写代码的好处我没看出来 throw new IndexOutOfBoundsException(); } return 0;//要从PushbackReader 对象中读取的len个字符0时返回0 } int avail buf.length - pos;//用于回退的字符数组缓冲区中实际装载了buf.length - pos个字符 if (avail 0) { if (len avail) avail len; System.arraycopy(buf, pos, cbuf, off, avail); pos avail; off avail; len - avail; } if (len 0) { len super.read(cbuf, off, len); if (len -1) { return (avail 0) ? -1 : avail; } return avail len;//场景④中的x就是这里的avail len } return avail; } catch (ArrayIndexOutOfBoundsException e) { throw new IndexOutOfBoundsException(); } } } //一次只可以回推1个字符数据到char[] buf用于回退的字符数组缓冲区中 public void unread(int c) throws IOException { synchronized (lock) { ensureOpen(); if (pos 0)//pos0时表示char[] buf用于回退的字符数组缓冲区中已经没有足够的容量再放置数据所以抛出一个IOException异常。 throw new IOException(Pushback buffer overflow); buf[--pos] (char) c; } } ...省略部分代码... }如果使用者使用的被装饰的字符输入流是CharArrayReader然后执行PushbackReader.class的read()函数和unread()函数时使用如下代码package com.xxx.bio; import java.io.CharArrayReader; import java.io.IOException; import java.io.PushbackReader; import java.io.Reader; public class PushbackReaderTest { public static void main(String[] args) throws IOException { String str 你好世界我将征服你; Reader reader new CharArrayReader(str.toCharArray()); //构建字符回退流 PushbackReader pushbackReader new PushbackReader(reader, 8); int len -1; System.out.println(输出内容:); while ((len pushbackReader.read()) ! -1) { //转为char类型 char c (char) len; if (c ) {//此处为中文 //为号时 先往前读3个再往后倒两个 char[] b1 new char[3]; pushbackReader.read(b1); //往后倒两个 pushbackReader.unread(b1, 0, 2); } else { System.out.print(c); } } } }上面代码的执行结果如下以上代码的整个执行过程分为以下5步①、通过构造函数构建一个长度为8的char[] buf用于回退的字符数组缓冲区和CharArrayReader.class类型的被装饰的字符输入流如下所示PushbackReader pushbackReader new PushbackReader(reader, 8);②、按照顺序从CharArrayReader.class类型的输入流中读取字符直到读取到中文“”时如下所示while ((len pushbackReader.read()) ! -1) { //转为char类型 char c (char) len; if (c ) {//此处为中文 } else { System.out.print(c); } }