1. 注释解释:
A character-stream reader that allows characters to be pushed back into the stream.
一个允许字符被推回到流中的字符流读取器。(可能“推回”这个词不太准确)
根据我们以往对输入流的的认知,它应该是单向的,被从头到尾依次读取。即使有些流 提供 mark 功能,那也是造成对某一部分的重复读取,不会产生流数据源本身不包含的字符。但是 PushbackReader 不一样,它允许调用者将一些数据源本身不包含的字符插入到流的任意位置,下面我们来看看实现原理。
2. 定义
首先我们来看看它的定义和构造方法:
可以看出,PushbackReader 继承自 FilterReader,查看过 FilterReader 源码,只是对 Reader 的一个通用包装类,没有什么特殊功能的添加(可能只是为了以后扩展)。而想要构造一个有意义 PushbackReader,必须向构造方法传递一个 Reader 实例(否则 ensureOpen 方法校验不通过),构造方法会初始化一个回退缓冲区和缓冲区位置指针,这两个便是 PushbackReader 的关键字段了。
3. 核心方法
3.1. read 方法
可以看出,read 方法都会优先从缓冲区读取数据。其中,缓冲区 pos 指针在最后表示缓冲区没有数据了。那么缓冲区的数据哪里来的呢?下面看看 unread 方法。
3.2. unread 方法
unread 方法就是该类最核心的方法了,它允许调用者通过该方法将数据填充到缓冲区,然后在 read 时被优先读取。
可以看出,unread 方法将数据从缓冲区的后面向前面填充到缓冲区,然后 read 从前向后读取,类似一个栈结构。但是注意,单次回推的字符序列并不会逆序,而是作为一个整体。所以 unread 和 read 并不是一个简单的逆序过程。
4. 总结
4.1. PushbackReader 提供 unread 方法,允许使用者将自定义的数据插入到输入流中;
4.2. 插入的数据被保存在缓冲区中,一旦缓冲区中有数据插入,那么下次读取将会优先读取缓冲区;
4.3. 后插入的数据,先读取(但单次插入的并不会被逆序);
5. 举例
假如输入流源数据为字符串 “123456789”,那么当读取 “123” 之后分别执行 unread 推回这几个串:“abc”、“de”、“fghi”,那么最终读取完成后的结果就是 “123fghideabc456789” 。
6. 验证
下面我们来编写代码测试一下,为了简单起见我把被包装的 Reader 类使用 StringReader :
public class PushbackReaderTest { public static void main(String[] args) throws IOException { StringReader stringReader = new StringReader("123456789"); PushbackReader pushbackReader = new PushbackReader(stringReader, 100); StringBuilder stringBuilder = new StringBuilder(); char[] buff = new char[100]; //第一步,首先读取 “123” int n = pushbackReader.read(buff, 0, 3); stringBuilder.append(buff); System.out.println("第一步,读取了 " + n + " 个字符:"); System.out.println(buff); //第二步,unread:abc, de, fghi pushbackReader.unread(new char[]{'a', 'b', 'c'}); pushbackReader.unread(new char[]{'d', 'e'}); pushbackReader.unread(new char[]{'f', 'g', 'h', 'i'}); int c = 0; while (c != -1){ c = pushbackReader.read(); stringBuilder.append((char) c); } System.out.println("最终读取的数据:" + stringBuilder.toString()); } }
测试结果: