ByteBuffer 无读写模式开关,行为由 position、limit、capacity 决定;flip() 将写转读(limit=position, position=0);clear() 重置为全可写;compact() 保留未读数据并压缩至开头;rewind() 仅 position=0 重读;mark()/reset() 支持位置标记与回退。

Java NIO 中的 Buffer(尤其是 ByteBuffer)本身没有“读模式”或“写模式”的开关,它的读写行为完全由内部三个指针的状态决定:position、limit 和 capacity。切换本质是调整这些指针,而不是调用某个魔法方法开启/关闭模式。
flip():写完转读的唯一正确操作
当你向 Buffer 写入数据后(比如通过 channel.read(buffer) 或 buffer.put()),position 会移到已写数据末尾,limit 仍等于 capacity。此时若直接 get(),要么读不到数据(position 已超限),要么抛 BufferUnderflowException。
调用 flip() 才能安全进入读状态,它只做两件事:
- limit = position —— 把“可读边界”设为当前已写入的数据长度
-
position = 0 —— 让下一次
get()从头开始读
注意:flip() 不清空、不复制、不改变 capacity,只是封存当前写入成果。重复调用会导致 limit 被不断压低(比如第二次 flip 后 limit = 0),再读就什么都没有了。
立即学习“Java免费学习笔记(深入)”;
clear() 和 compact():读完后如何回到可写状态
读完所有数据后,需要重置 Buffer 以便再次写入。这里有两个选择,用途完全不同:
- clear():适用于“整包已处理完毕”的场景。它把 position = 0,limit = capacity,相当于把缓冲区当全新的一样用。之前写过的数据还在底层数组里,但指针已移开,后续写入会覆盖。
-
compact():专为“TCP 粘包/半包”设计。比如 read() 只收到前半条消息,你已读了部分字节(position 在中间),但后半截还没来。这时不能
flip()(会丢后半截),也不能clear()(前半截白读了)。compact()会把 position 到 limit 之间未读的数据拷贝到开头,然后设置 position = 已拷贝字节数,limit = capacity,为下次 write 做准备。
注意:compact() 是内存拷贝操作,大数据量时有开销;执行后 Buffer 处于“可写状态”,如需立即读,还得再调一次 flip()。
rewind():不是模式切换,而是重读一遍
rewind() 只做一件事:position = 0,limit 和 capacity 完全不变。它不改变读写语义,只是让你从头再读一次当前有效数据范围(即 [0, limit) 区间)。
典型用途:
- 刚读了一半,想重新解析整个有效内容
- 写了一半,想检查开头几个字节是否符合协议头
它和 flip() 容易混淆,但语义完全不同:flip 是“写→读”的状态跃迁,rewind 是“读→再读”的位置回退。
别忘了 mark 和 reset:灵活跳转的辅助工具
如果在读或写过程中想临时记住某个位置,可以用 mark() 标记当前 position;之后任何时候调用 reset(),就能把 position 回退到那个标记点。
这个机制不参与模式切换,但能提升解析灵活性,比如跳过协议头、解析字段后再返回校验魔数等。


















