一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Java中 InputStream.markSupported mark 与 reset 如何实现流的重复读取

时间:2026-07-20 17:52:49 编辑:袖梨 来源:一聚教程网

Java InputStream 的 mark/reset 机制支持有限回退读取,前提是流实现 markSupported() 返回 true;mark(int readlimit) 设置标记并声明有效期,reset() 回退到标记位,但受缓冲区和 readlimit 限制。

Java 中的 InputStream 默认不支持重复读取,但通过 mark()reset() 配合 markSupported(),可以在一定条件下实现“回退”式读取——本质是让流回到上次标记的位置,从而重新读取已消费的数据。

markSupported() 是前提判断

不是所有输入流都支持标记和重置。调用 markSupported() 返回 true 才能安全使用 mark()reset()。例如:

  • BufferedInputStream 支持(内部维护缓冲区)
  • ByteArrayInputStream 支持(底层是数组,可任意跳转)
  • FileInputStream 默认不支持(底层是文件通道,无内置缓冲和标记机制)
  • ObjectInputStreamFilterInputStream 子类需看具体实现

如果流不支持却强行调用 mark()reset(),不会报错,但 reset() 会抛出 IOException(如 “mark invalid”)。

mark(int readlimit) 的作用与限制

mark(int readlimit) 在当前位置设一个标记,并声明“从该点起,最多再读 readlimit 字节后,这个标记仍有效”。它不是无限缓存,而是告诉流:“你至少要能记住接下来 readlimit 字节内的数据,以便后续 reset()。”

立即学习“Java免费学习笔记(深入)”;

  • readlimit 不是缓冲区大小,而是“标记有效期”的提示值
  • 实际能否成功重置,取决于流的具体实现和当前缓冲能力(例如 BufferedInputStream 的缓冲区大小是否足够容纳已读+待回退的数据)
  • 多次调用 mark() 会覆盖前一个标记

reset() 的行为与注意事项

reset() 将流的读取位置回退到最近一次有效 mark() 的位置。成功后,后续 read() 会从标记处重新开始读取。

  • 必须在标记有效期内调用(即未超过 readlimit 字节数,且中间未发生不可恢复的操作)
  • 某些流(如 BufferedInputStream)在缓冲区被填满或重用时可能自动失效标记
  • reset() 不影响已读出的字节,只改变流内部指针;之前读到的数据需要你自己缓存或丢弃

实用示例:用 BufferedInputStream 实现重复读取

这是最常见也最可靠的用法:

InputStream is = new FileInputStream("data.txt");BufferedInputStream bis = new BufferedInputStream(is, 8192); // 显式指定足够缓冲区if (bis.markSupported()) {    bis.mark(1024); // 声明:接下来 1KB 内允许 reset    int b1 = bis.read(); // 读第一个字节    int b2 = bis.read(); // 读第二个字节    // …… 做些判断逻辑,发现需要重读    bis.reset(); // 回到 mark 处    int b1Again = bis.read(); // 又读到第一个字节}

注意:若中间读了超过 1024 字节,或缓冲区被刷新/重用,reset() 可能失败。

本质上,mark/reset 不是通用的“重放”机制,而是一种轻量级的、有范围限制的回溯能力,依赖流实现的缓冲策略。真正需要多次遍历,更稳妥的方式仍是把数据读入 byte[]ByteArrayInputStream

热门栏目