bytebuffer 默认不支持 mark/reset 因其为高性能零拷贝设计,mark/reset 会引入额外状态开销;仅 marksupported() 返回 true 时才可用,替代方案是手动保存并恢复 position。

Buffer 的 mark() 和 reset() 方法可用于在读取过程中临时标记位置,并在需要时回退到该位置,从而实现对同一段数据的重复读取。但要注意:并非所有 Buffer 子类都默认支持 mark/reset —— 只有调用过 markSupported() == true 的缓冲区才可用;而 Java 标准库中,ByteBuffer 默认不支持(返回 false),需手动包装或使用其他方式模拟。
为什么 ByteBuffer 默认不支持 mark/reset?
因为 ByteBuffer 是为高性能、零拷贝设计的底层容器,mark/reset 会引入额外状态管理开销。JDK 中只有部分带内部读写状态的封装类(如 java.io.BufferedInputStream 包装的流)才启用该机制。直接使用 ByteBuffer 时,mark() 和 reset() 是空操作,调用后无效果。
替代方案:用 position 与 limit 手动保存/恢复读取位置
对 ByteBuffer,最常用且可靠的方式是显式记录关键位置(如 position()),之后通过 position(int) 回退:
- 读取前调用
int markPos = buffer.position();保存当前位置 - 执行任意读取操作(如
get()、getChar()、get(byte[])等) - 需要重读时调用
buffer.position(markPos); - 注意:确保
markPos ≤ buffer.limit(),否则抛IllegalArgumentException
若坚持用 mark/reset:可自定义支持的 ByteBuffer 包装器
通过继承或委托方式扩展功能,例如:
- 创建一个
MarkableByteBuffer类,内部缓存一个markPosition字段 - 重写
mark():仅保存当前position - 重写
reset():将position设为保存的markPosition - 重写
markSupported()返回true - 注意:需同步处理
flip()、compact()等会改变 position/limit 的操作,避免 mark 失效
实际读取重复场景示例(推荐手动 position 控制)
比如解析协议头含长度字段,需先读 4 字节长度,再读对应字节数,但又想保留原始 header 数据供后续日志打印:
int headerStart = buf.position();int len = buf.getInt(); // 读取长度,position 自动后移 4byte[] payload = new byte[len];buf.get(payload); // 继续读 payload// 想再次访问完整 header(4 字节长度域):buf.position(headerStart).limit(headerStart + 4);ByteBuffer headerView = buf.slice(); // 得到只含 header 的视图










