bufferedinputstream 的 mark() 和 reset() 方法可实现回退读取,但需满足数据仍在缓冲区且未超 readlimit;其原理是记录读取位置并限制后续可读字节数,超限或缓冲区滑动将使 mark 失效。

BufferedInputStream 的 mark() 和 reset() 方法可以实现“回退读取”,但必须满足前提:数据仍在内部缓冲区中,且未超出 readlimit 限制。
mark() 和 reset() 的工作原理
BufferedInputStream 内部维护一个缓冲区(默认 8192 字节)和一个读取位置指针。调用 mark(int readlimit) 时,它会记录当前读取位置,并承诺:只要从 mark 位置起后续最多读取 readlimit 字节,就还能通过 reset() 回退到该位置。一旦实际读取字节数超过 readlimit,或缓冲区被填满后发生重载(如继续读导致缓冲区滑动),mark 就失效,reset() 会抛出 IOException。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
正确使用的三个关键点
- 必须在支持 mark 的流上包装:底层流(如 FileInputStream)本身可能不支持 mark/reset;BufferedInputStream 提供了缓冲层的 mark 支持,但前提是底层流允许跳转(如文件流通常支持,网络流一般不支持)。
-
mark() 要在读取前调用:例如想重复读前 100 字节,应在第一次读之前调用
mark(100),而不是边读边 mark。 - readlimit 要合理设置:它不是缓冲区大小,而是“从 mark 位置起最多允许读多少字节后仍能 reset”。若设为 1024,但你读了 1500 字节再 reset,就会失败。
一个可运行的小例子
以下代码读取文件前 10 字节两次:
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.txt"))) {
bis.mark(20); // 允许最多读 20 字节后 reset
// 第一次读取
byte[] buf1 = new byte[10];
int n1 = bis.read(buf1);
System.out.println("第一次读:" + new String(buf1, 0, n1));
// 回退到 mark 位置
bis.reset();
// 第二次读取(相同内容)
byte[] buf2 = new byte[10];
int n2 = bis.read(buf2);
System.out.println("第二次读:" + new String(buf2, 0, n2));
}
注意:如果文件开头不足 10 字节,或中间调用了超过 20 字节的读操作,reset() 就会失败。
常见陷阱与替代方案
- 多次
mark()只保留最后一次,之前的 mark 会被覆盖。 - 调用
skip(long)或available()不影响 mark 状态,但实际读取(read()系列)会推进位置。 - 若需任意次数/位置重读,BufferedInputStream 的 mark/reset 不够灵活;更稳妥的方式是把需要重复读的数据先复制到 byte[] 或 ByteArrayOutputStream 中,再构造新的 ByteArrayInputStream 读取。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










