ByteBuffer.wrap 是零拷贝包装,不复制数据,直接用原 byte[] 作为底层存储,修改缓冲区会同步影响原数组;默认 position=0、limit=capacity=数组长度,mark=-1;需 flip() 才能读取写入内容;仅适用于生命周期可控的数组,多线程或长期持有时应 clone 避免风险。

ByteBuffer.wrap 本质是零拷贝包装,不是复制数据
ByteBuffer.wrap 不会创建新数组,只是用已有 byte[] 作为底层存储,直接构造一个可读写的缓冲区视图。这意味着修改缓冲区内容会同步反映在原数组上,反之亦然——这是它高效的原因,也是最易出错的点。
- 如果原数组后续被其他逻辑复用或修改,缓冲区行为可能变得不可预测
- 调用
wrap(byte[])后,position默认为 0,limit和capacity都等于数组长度,但mark未设置(为 -1) - 若需只暴露部分字节(比如跳过头部 4 字节),应改用
wrap(byte[], offset, length),否则得手动调用position(4).limit(4 + payloadLen)
写入后必须调用 flip() 才能读取
刚用 wrap 得到的缓冲区处于“写模式”:你可以用 put() 写入,但此时 position 会推进,limit 仍卡在容量尾部。若想立刻读取已写内容,必须显式 flip() —— 它把 limit 设为当前 position,再把 position 归零。
- 常见错误:包装后直接
get(),结果抛BufferUnderflowException,因为position == 0但limit == capacity,看似可读,实则未写入任何有效数据 - 正确流程示例:
byte[] data = new byte[10]; ByteBuffer bb = ByteBuffer.wrap(data); bb.put((byte)0x01).put((byte)0x02); // 写入 2 字节 bb.flip(); // 关键!准备读 byte b1 = bb.get(); // → 0x01 byte b2 = bb.get(); // → 0x02
注意 wrap 后的缓冲区无法扩容,且 array() 返回原始引用
ByteBuffer.wrap 生成的是 heap buffer,其底层 array() 就是传入的原始数组。这意味着:
- 调用
bb.array()可直接拿到原数组,但若缓冲区是只读的(由asReadOnlyBuffer()创建),则抛ReadOnlyBufferException - 不能通过
compact()或clear()改变数组大小——这些操作只重置缓冲区指针,数组本身长度固定 - 如果需要动态扩容,
wrap不适用,应改用ByteBuffer.allocate()或结合Arrays.copyOf()手动处理
与 allocate 的关键区别:是否持有数组所有权
ByteBuffer.allocate(n) 分配新数组并独占使用;而 wrap(arr) 完全不拥有数组,仅借用。这导致两个典型问题:
- 若原数组被 GC(比如它是某个局部方法的栈内临时数组),而缓冲区还在被长期持有,就会引发诡异的内存污染或越界读——实际不会 crash,但读到的是后续分配对象的残留数据
- 多线程场景下,若多个线程同时用
wrap包装同一数组并并发读写,无任何同步机制,结果完全不可控 - 安全做法:确保被
wrap的数组生命周期 ≥ 缓冲区使用周期;必要时用ByteBuffer.wrap(arr.clone())切断引用,代价是拷贝开销
wrap 最容易被忽略的复杂点。

















