一 nio中的bytebuffer的认识

1.bytebuffer的数据结构

对于ByteBuffer,其主要有五个属性:mark,position,limit,capacity和array。这五个属性的作用如下:
mark:记录了当前所标记的索引下标;
position:对于写入模式,表示当前可写入数据的下标,对于读取模式,表示接下来可以读取的数据的下标;
limit:对于写入模式,表示当前可以写入的数组大小,默认为数组的最大长度,对于读取模式,表示当前最多可以读取的数据 的位置下标;
capacity:表示当前数组的容量大小;
array:保存了当前写入的数据。
这几个数据中,除了array是用于保存数据的以外,这里最终的主要是position,limit和capacity三个属性,因为对于写入和读取模式,这三个属性的表示的含义大不一样。

1.1写入模式:

如下图所示为初始状态和写入3个字节之后position,limit和capacity三个属性的状态:

Java Bytecode Decompiler安装教程_System


从图中可以看出,在写入模式下,limit指向的始终是当前可最多写入的数组索引下标,position指向的则是下一个可以写入的数据的索引位置,而capacity则始终不会变化,即为数组大小。

注意:图片来源知乎,如果侵权,联系我删除

1.2读取模式
假设我们按照上述方式在初始长度为6的ByteBuffer中写入了三个字节的数据,此时我们将模式切换为读取模式,那么这里的position,limit和capacity则变为如下形式:

Java Bytecode Decompiler安装教程_System_02


可以看到,当切换为读取模式之后,limit则指向了最后一个可读取数据的下一个位置,表示最多可读取的数据;position则指向了数组的初始位置,表示下一个可读取的数据的位置;capacity还是表示数组的最大容量。这里当我们一个一个读取数据的时候,position就会依次往下切换,当期与limit重合时,就表示当前ByteBuffer中已没有可读取的数据了。

注意:图片来源知乎,如果侵权,联系我删除

1.3下面用代码来说明相关api

flip:切换为读取模式
mark:这个属性是一个标识的作用,即记录当前position的位置,
在后续如果调用reset()或者flip()方法时,ByteBuffer的position就会被重置到mark所记录的位置。
因而对于写入模式,在mark()并reset()后,将会回到mark记录的可以写入数据的位置;
对于读取模式,在mark()并reset()后,将会回到mark记录的可以读取的数据的位置
代码打印运行结果:

ByteBuffer buffer = ByteBuffer.allocate(6);
        System.out.println(buffer);
        
        buffer.put((byte) 1);
        buffer.put((byte) 2);
        buffer.put((byte) 3);

        buffer.mark();
        buffer.put((byte) 4);

        System.out.println("put后=======>" + buffer);

        buffer.reset();
        System.out.println("reset后=======>" + buffer);


        buffer.flip();
        System.out.println("flip后===>" + buffer);
        
        buffer.get();
        System.out.println("get后===>" + buffer);

        buffer.mark();
        System.out.println("再次mark后===>" + buffer);


        buffer.reset();
        System.out.println("再次reset后===>" + buffer);

运行结果如下:
java.nio.HeapByteBuffer[pos=0 lim=6 cap=6]
put后=======>java.nio.HeapByteBuffer[pos=4 lim=6 cap=6]
reset后=======>java.nio.HeapByteBuffer[pos=3 lim=6 cap=6]
flip后===>java.nio.HeapByteBuffer[pos=0 lim=3 cap=6]
get后===>java.nio.HeapByteBuffer[pos=1 lim=3 cap=6]
再次mark后===>java.nio.HeapByteBuffer[pos=1 lim=3 cap=6]
再次reset后===>java.nio.HeapByteBuffer[pos=1 lim=3 cap=6]

二:netty中的bytebuf

1.数据结构

Java Bytecode Decompiler安装教程_数据_03


可以看出提供了两个pointer来操作buf。

2.相关api

因api太多,我只是用demo测试一下,常用的api。如果想看更详细的请移步官网https://netty.io/3.10/api/index.html

ByteBuf buf = Unpooled.buffer();
        byte[] bytes = "zpf wants to be a good coder".getBytes();
        buf.writeBytes(bytes);
        System.out.println("写入byte数组后的buf==>"+buf);

        int readCount = buf.readableBytes();
        System.out.println("可读的长度为==>"+readCount);


        byte[] readableBytes = new byte[readCount];

        buf.readBytes(readableBytes);
        System.out.println("调用buf.read之后的byteBuf==>"+buf);


        ByteBuf clear = buf.clear();
        System.out.println("clear后==>"+buf);
        System.out.println("clear后的clear==>"+clear);


        ByteBuf discardReadBytes = buf.discardReadBytes();


        System.out.println("调用discardReadBytes后的buf==>"+buf);
        System.out.println("discardReadBytes的值==>"+discardReadBytes);



        String s = new String(readableBytes,"UTF-8");
        System.out.println("再转回来String==>"+s);

运行结果如下:
写入byte数组后的buf==>UnpooledHeapByteBuf(ridx: 0, widx: 28, cap: 256)
可读的长度为==>28
调用buf.read之后的byteBuf==>UnpooledHeapByteBuf(ridx: 28, widx: 28, cap: 256)
clear后==>UnpooledHeapByteBuf(ridx: 0, widx: 0, cap: 256)
clear后的clear==>UnpooledHeapByteBuf(ridx: 0, widx: 0, cap: 256)
调用discardReadBytes后的buf==>UnpooledHeapByteBuf(ridx: 0, widx: 0, cap: 256)
discardReadBytes的值==>UnpooledHeapByteBuf(ridx: 0, widx: 0, cap: 256)
再转回来String==>zpf wants to be a good coder

三:nio中的byteBuffer和netty中的bytebuf的区别

1.ByteBuffer必须自己长度固定,一旦分配完成,它的容量不能动态扩展;ByteBuf默认容器大小为256,支持动态扩容,在允许的最大扩容范围内(Integer.MAX_VALUE)。

2.ByteBuffer只有一个position标识位置的指针,读的时候需要手动的调用flip()和rewind()等,否则很容易导致程序处理失败。而ByteBuf有两个标识位置的指针,一个写writerIndex,一个读readerIndex,读写的时候不需要调用额外的方法。

3.NIO的SocketChannel进行网络读写时,操作的对象是JDK标准的java.nio.byteBuffer。由于Netty使用统一的ByteBuf替代JDK原生的java.nio.ByteBuffer,所以ByteBuf中定义了ByteBuffer nioBuffer()方法将ByteBuf转换成ByteBuffer。

嗯,应该还有差异,以上是我目前的了解,最近在看netty权威指南,如果有新的理解,会在及时记录一下。