这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战
ByteBuffer中的核心属性
- position 缓冲区读取的位置
- limit 目前缓冲区的可读位置
- capacity 缓冲区的容量
- mark 在缓冲区的某一位置打标记
申请一块1024大小的ByteBuffer
可以通过ByteBuffer提供的静态方法直接申请基于byte数组的缓冲区ByteBuffer.allocate(capacity)和基于堆外内存的缓冲区ByteBuffer.allocateDirect(capacity)。
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
System.out.println(byteBuffer);
输出:
java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]
因为是新申请的byteBuffer,所以,limit=capacity,position=0
向ByteBuffer写入数据
写入helloworld字符到ByteBuffer
byteBuffer.put("helloworld".getBytes(StandardCharsets.UTF_8));
System.out.println(byteBuffer);
输出:
java.nio.HeapByteBuffer[pos=10 lim=1024 cap=1024]
因为helloworld的长度为10,所以,position的位移变为10,limit和capacity都没有变化
向ByteBuffer读数据
JDK提供的缓冲区,读写数据用的都是position指针,所以,在读数据之前,需要将position指针通过调用flip方法移动到缓冲区起始位置。
byteBuffer.flip();
System.out.println(byteBuffer);
byte[] data = new byte[5];
byteBuffer.get(data);
System.out.println(new String(data,StandardCharsets.UTF_8));
System.out.println(byteBuffer);
输出:
java.nio.HeapByteBuffer[pos=0 lim=10 cap=1024]
hello
java.nio.HeapByteBuffer[pos=5 lim=10 cap=1024]
- 调用
flip方法后,可以看到不仅将position重置,而且将limit的值设置为原writePosition时的位置。 - 读取5个byte后,position跟我们预期的保持一致,在坐标为5的位置。
mark的作用
mark相当于在缓冲区的某个位置做一个标记,然后进行读取操作,当需要将指针放到刚才mark的位置时,调用reset方法即可。
byteBuffer.mark();
System.out.println(byteBuffer);
byte[] data1 = new byte[5];
byteBuffer.get(data1);
System.out.println(new String(data1,StandardCharsets.UTF_8));
System.out.println(byteBuffer);
byteBuffer.reset();
System.out.println(byteBuffer);
可以看出,position指针由原来的5读取完5个byte后变为10,通过reset后又变为了5
Rewind和Flip的区别
Rewind和Flip的源码如下:
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
- 这两个API都可以将position的值和mark的值重置,
- Rewind不会设置limit的大小,但是flip会将原position的值赋值给limit
ByteBuffer的缺点
- 申请的内存空间大小是固定的不能动态的扩缩容
- 读取和写入的操作都是基于position,在实际应用场景中,会产生大量的调用
flip方法,开发人员要不断的根据程序的读写不停的操作position指针,编码体验较差, - 没有引入内存池的概念,频繁的创建和销毁ByteBuffer对系统开销较大。
关于ByteBuffer的介绍就说这么多,下一讲会介绍Netty中对ByteBuffer的优化。~~