NIO Buffer 缓冲区读写操作详解
基本概念
Buffer是NIO中存储数据的容器,本质是一个内存块,用于Channel之间的数据读写,核心属性:

| 属性 | 说明 |
|---|---|
| capacity | 缓冲区容量(固定不变) |
| position | 当前读写位置(从0开始) |
| limit | 可读/写的最大位置 |
| mark | 标记位置(可选) |
关系:0 <= mark <= position <= limit <= capacity |
核心读写模式
写模式(写入Buffer)
// 1. 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
// 2. 写入数据
buffer.put((byte) 65); // position=1
buffer.put("Hello".getBytes()); // position=6
// 3. 切换到读模式
buffer.flip(); // limit=position, position=0
读模式(从Buffer读取)
// 4. 读取数据 byte b = buffer.get(); // position=1 ByteBuffer slice = buffer.slice(); // 读取剩余数据 // 5. 重复读取(重置position) buffer.rewind(); // position=0, limit不变 // 6. 清空缓冲区 buffer.clear(); // position=0, limit=capacity
关键方法详解
写入方法
// 绝对写入(指定位置) buffer.put(0, (byte) 65); // 相对写入(当前位置) buffer.put((byte) 66); // position+1 buffer.putInt(123); // 写入int(4字节) buffer.putLong(456L); // 写入long(8字节) // 批量写入 byte[] data = "World".getBytes(); buffer.put(data, 0, data.length);
读取方法
// 绝对读取 byte b = buffer.get(0); // 不改变position // 相对读取 byte b = buffer.get(); // position+1 int i = buffer.getInt(); // 读取int long l = buffer.getLong(); // 读取long // 批量读取 byte[] dst = new byte[5]; buffer.get(dst, 0, 5); // 读取5字节到数组
状态切换方法
| 方法 | 功能 | 修改的属性 |
|---|---|---|
flip() |
写→读 | limit=position, position=0 |
clear() |
任意→写 | position=0, limit=capacity |
rewind() |
重复读 | position=0, limit不变 |
compact() |
压缩未读数据 | 保留未读数据,准备写入 |
完整示例
public class BufferDemo {
public static void main(String[] args) {
// 1. 分配缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 2. 写入数据(写模式)
String data = "Hello NIO Buffer";
buffer.put(data.getBytes());
System.out.println("写入后: position=" + buffer.position()
+ ", limit=" + buffer.limit());
// 3. 切换到读模式
buffer.flip();
System.out.println("flip后: position=" + buffer.position()
+ ", limit=" + buffer.limit());
// 4. 读取数据
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes);
System.out.println("读取内容: " + new String(bytes));
// 5. 复读(rewind)
buffer.rewind();
byte[] reRead = new byte[5];
buffer.get(reRead);
System.out.println("重新读取前5字节: " + new String(reRead));
// 6. 清空并重新写入
buffer.clear();
System.out.println("clear后: position=" + buffer.position()
+ ", limit=" + buffer.limit());
// 7. 使用compact保留未读数据
buffer.put("New data".getBytes());
buffer.flip();
buffer.get(); // 读取1字节
buffer.compact(); // 保留未读数据
buffer.put(" appended".getBytes());
buffer.flip();
byte[] result = new byte[buffer.limit()];
buffer.get(result);
System.out.println("compact后内容: " + new String(result));
}
}
高级用法
只读缓冲区
ByteBuffer readOnly = buffer.asReadOnlyBuffer(); // readOnly.put(...) // 会抛出ReadOnlyBufferException
直接缓冲区(Direct Buffer)
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024); // 零拷贝,适合网络传输,但创建/销毁开销大
缓冲区分片
buffer.position(2); buffer.limit(6); ByteBuffer slice = buffer.slice(); // 共享底层数据 slice.put(0, (byte) 88); // 会影响原buffer
性能优化建议
- 合理分配大小:避免过小导致频繁扩容,避免过大浪费内存
- 重用缓冲区:使用
clear()或compact()而不是重新分配 - 批量操作:优先使用批量put/get数组操作
- 直接缓冲区:I/O密集型场景考虑DirectBuffer
- 避免碎片:使用
compact()而不是clear()保留未读数据
常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
| BufferUnderflowException | 读取超出position | 检查remaining() |
| BufferOverflowException | 写入超出limit | 检查剩余空间 |
| IllegalArgumentException | 创建负容量 | 检查分配参数 |
| ReadOnlyBufferException | 修改只读缓冲区 | 检查isReadOnly() |
读写操作核心流程:
写入数据 → flip() → 读取数据 → clear()/compact() → 循环
关键原则:
- 写模式:position指向写入位置,limit=capacity
- 读模式:position指向读取位置,limit=数据末尾
- 切换模式:必须调用
flip()或clear()
通过深入理解这些概念,您可以高效地使用NIO Buffer进行各种I/O操作。