单次 unread() 不够用,因为 PushbackReader 缓冲区按栈式结构从头写入、从头读取,仅能回退一个字符;判断双字符操作符(如"==")需先读两个字符再整体回退,必须两次 unread() 配合顺序控制才能实现可靠预读。

PushbackReader 本身不直接支持双字符 lookahead,但可通过两次 unread() 实现——关键在于“先读再退”,且必须严格控制退格顺序和数量。
为什么单次 unread() 不够用
Java 的 PushbackReader 底层维护一个固定大小的 pushback 缓冲区(默认 1 字符),调用 unread(int c) 会把字符压入缓冲区头部。若需判断如 "=="、"!="、" 这类双字符运算符,仅靠一次 unread 无法还原两个已读字符的状态:第一次 read 拿走第一个字符,第二次 read 拿走第二个,此时流指针已前进两位,必须把两个字符都退回去才能交由后续逻辑统一匹配。
正确实现双字符探查的三步法
以识别 "==" 为例,在词法分析器中应这样操作:
- 先调用
read()获取第一个字符(比如得到'=') - 再调用
read()获取第二个字符(比如得到'=') - 若确认是双字符 token,则按**逆序**两次调用
unread():先退第二个字符,再退第一个字符
注意:必须逆序 unread,因为 pushback 是栈式行为——后 unread 的字符会先被下次 read 取出。若顺序写成 unread(first); unread(second),下次 read 会先拿到 first,破坏原始顺序。
立即学习“Java免费学习笔记(深入)”;
避免缓冲区溢出的实际约束
默认构造的 PushbackReader 只支持 1 字符 pushback,双字符探查会直接抛 IOException("Push back buffer is full")。必须显式指定足够容量:
- 创建时传入 size ≥ 2,例如
new PushbackReader(reader, 2) - 若需支持更长前缀(如三字符如
"==="或注释"/*"),缓冲区 size 至少为最大 lookahead 长度 - 缓冲区大小不可动态调整,应在初始化时预估好
与 Scanner / 自定义 Reader 的取舍
PushbackReader 适合轻量、流式、逐字符驱动的解析器;但它要求手动管理 unread 状态,容易出错:
- 未匹配时忘记 unread 会导致字符丢失
- 多次 unread 超出缓冲区引发异常
- 多线程不安全,需外部同步
更稳健的做法是封装一层 PeekableReader,内部用 char[] 缓存已读未消费字符,提供 peek(int n) 和 consume() 方法,把 lookahead 逻辑与业务解耦。不过对于简单表达式解析,原生 PushbackReader 配合 careful unread 已足够有效。


















