PushbackReader 是 Java 中用于回退已读字符的装饰器类,通过内部缓冲区实现 unread() 操作,支持 LIFO 顺序回退、构造时指定缓冲区大小,适用于词法解析等需预读回退的场景。

PushbackReader 是 Java 中用于“回退”已读字符的装饰器类,它包裹一个底层字符输入流(如 InputStreamReader 或 StringReader),允许你把刚读取的一个或多个字符“推回去”,以便后续再次读取。这在实现词法解析、语法预读(如 peek 操作)、处理分隔符或自定义协议时非常有用。
核心机制:unmark() 不适用,靠 unread() 回退
不同于 BufferedReader 的标记/重置(mark()/reset()),PushbackReader 不依赖标记位置,而是通过内部缓冲区显式“ unread ”字符:
- 调用
read()读取一个字符后,可立即用unread(int c)把它推回; - 也可一次推回多个字符,用
unread(char[] buf, int off, int len); - 内部缓冲区大小有限(构造时指定,默认为 1),超出会抛
IOException; - 推回的字符会在下次
read()时最先被返回,遵循 LIFO(后推先读)顺序。
典型使用场景:解析带前缀的 token
比如解析形如 "#include <stdio.h>" 的行,需要识别 # 后跟标识符,但又不确定后面是否是合法关键字——可先读一个字符判断,再决定是否回退:
try (PushbackReader pbr = new PushbackReader(
new StringReader("#include <stdio.h>"), 2)) {
int ch = pbr.read(); // 读到 '#'
if (ch == '#') {
ch = pbr.read(); // 读下一个,比如 'i'
if (Character.isLetter(ch)) {
pbr.unread(ch); // 把 'i' 推回,留给后续词法分析器处理
System.out.println("发现预处理指令,已回退首字母");
}
}
}
注意事项与常见陷阱
-
缓冲区容量必须足够:若需回退 3 个字符,构造时要指定
new PushbackReader(reader, 3),否则unread()会失败; -
不能 unread -1(EOF):
unread(-1)无效,且read()返回 -1 表示流结束,不可推回; -
不支持 mark/reset 组合:它和
markSupported()无关,不要混用; -
线程不安全:多线程并发读写同一
PushbackReader需外部同步。
替代方案对比:何时选 PushbackReader?
如果只需 peek 一个字符,BufferedReader + mark(1) + reset() 更轻量;但若需多次 unread、灵活控制回退数量或底层流不支持 mark,则 PushbackReader 是更直接可靠的选择。它语义清晰,专为“推回”设计,不易误用。
立即学习“Java免费学习笔记(深入)”;


















