duplicate()不会创建只读缓冲区,它返回共享底层数据但状态独立的可写副本;需链式调用asreadonlybuffer()才能获得真正只读视图。

Java NIO 中的 duplicate() 方法本身**不会创建只读缓冲区**,它返回的是一个与原缓冲区共享底层数据、但拥有独立位置、限制和标记的新缓冲区,且该副本**默认仍是可写的**。若需要只读视图,必须在调用 duplicate() 后显式调用 asReadOnlyBuffer()。
duplicate() 的本质:共享数据 + 独立状态
duplicate() 创建的缓冲区与原缓冲区指向同一块底层数组(或堆外内存),因此修改其中一个缓冲区的内容(如 put 操作)会影响另一个;但它们各自维护自己的 position、limit、mark 和 capacity,互不干扰。
- 两个缓冲区的
isReadOnly()都返回false(除非原缓冲区本身就是只读的) - 对副本执行
put()会修改共享数据,可能影响原缓冲区的后续 get 操作 - 改变副本的 position 不会影响原缓冲区的位置
如何得到只读的 duplicate 视图
正确做法是链式调用:originalBuffer.duplicate().asReadOnlyBuffer()。其中 asReadOnlyBuffer() 返回一个真正不可写的新缓冲区,它继承原缓冲区(即 duplicate 出来的那个)的状态(position、limit 等),但禁止所有写操作。
- 该只读缓冲区仍与原始缓冲区共享底层数据,读取行为一致
- 尝试调用
put()、compact()、flip()(会修改写状态)等方法会抛出ReadOnlyBufferException -
isReadOnly()返回true
常见误用与注意事项
直接对只读缓冲区调用 duplicate() 得到的仍是只读副本;而对普通缓冲区只调用 duplicate() 并不能防止写入 —— 这是开发者常忽略的关键点。
- 错误写法:
ByteBuffer dup = buf.duplicate();→ dup 可写,不是只读 - 正确写法:
ByteBuffer readOnlyDup = buf.duplicate().asReadOnlyBuffer(); - 如果原缓冲区已是只读,
duplicate()返回的缓冲区自动只读,此时再调asReadOnlyBuffer()是冗余但安全的
实际使用示例
适合用于向第三方 API 提供缓冲区访问权限,又不想让对方意外修改数据的场景:
- 将某段解析中的 ByteBuffer 安全地传递给日志模块或校验工具
- 在多线程中分发同一份数据的只读视图,避免同步开销
- 构建不可变的数据快照(注意:仅保证 buffer 层面不可写,底层数组仍可能被其他引用修改)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











