• Java NIO ByteBuffer原理使用图文详解


    在Java网络开发的过程中接触NIO是必不可少的,在NIO中有一个重要的组件那就是 **ByteBuffer **,下面就来通过图文的方式来讲解ByteBuffer的使用以及一些操作的原理。

    1. ByteBuffer实现原理

    对于ByteBuffer来说主要有五个重要属性如下:

    • mark(int类型): 记录当前索引的位置
    • position(int类型): 读模式:表示接下来可以读取的数据位置, 写模式:表示可以写入数据的位置
    • limit(int类型): 读模式:表示可以写入数据大小,写模式:表示可以写入数据大小。 默认是ByteBuffer的capacity
    • capacity(int类型): ByteBuffer的容量
    • hb(byte array): 实际数据存储byte数组

    Tips: 几个数据之间的大小关系mark <= position <= limit <= capacity

    示意图如下:

    2. 读写模式

    ByteBuffer主要有读写模式,Java原生的和Netty的ByteBuf有不同的。ByteBuffer的读写模式需要自己进行切换。

    2.1 写模式

    写模式示意图如下:

    从上图可以看出来初始化后capacity是固定了。limit的值可以进行设置。当有新的数据写入position指针会进行移动。能写入的数据由limit确定。

    2.2 读模式

    读模式示意图如下:

    如何把写入的数据读取出来,首先要将写模式转换成成读的模式。否则会读模式会在在写的指针往后进行读取。随着数据读取position指针也会进行移动,limit会限制指针移动的位置。

    Tips: flip 方法用于读写模式切换

    对于ByteBuffer主要是弄清楚四个变量 position、limit、mark、capacity 四者之间的关系转换以及读写的关系转换。

    3. 使用示例

    下面会结合例子以及示图来说明ByteBuffer的一些基本使用和一些常见API的操作。如下是一个简单的使用示例:

    public class ByteBufferExample {
        public static void main(String[] args) {
            ByteBuffer allocate = ByteBuffer.allocate(20); //分配一个大小为20bytes的ByteBuffer
            System.out.println(allocate.capacity()); //20
            System.out.println(allocate.limit()); // 20
            System.out.println(allocate.position()); //0
            System.out.println("--------------------");
            allocate.putLong(10L); 
            System.out.println(allocate.capacity());//20
            System.out.println(allocate.limit());//20
            System.out.println(allocate.position());//8
            System.out.println("--------------------");
            System.out.println(allocate.getLong());
            System.out.println(allocate.capacity());//20
            System.out.println(allocate.limit());//20
            System.out.println(allocate.position());//16
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    不同的变量变化的示意图如下:

    上面代码中没有进行读写模式转换的。position指针不管读还是写会一直往capacity位置靠近。

    3.1 flip-API

    使用示例代码如下:

    public static void main(String[] args) throws Exception{
    
            ByteBuffer allocate = ByteBuffer.allocate(20); //分配一个大小为20bytes的ByteBuffer
            allocate.putLong(10L);
            System.out.println(allocate.capacity()); //20
            System.out.println(allocate.limit()); // 20
            System.out.println(allocate.position()); //8
            System.out.println("--------------------");
            allocate.flip();
            System.out.println(allocate.capacity()); //20
            System.out.println(allocate.limit()); // 8
            System.out.println(allocate.position()); //0
            System.out.println("--------------------");
            System.out.println(allocate.getLong()); //10
            System.out.println(allocate.capacity()); //20
            System.out.println(allocate.limit()); // 8
            System.out.println(allocate.position()); //8
            allocate.putLong(10L); //throw exception
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    示意图如下:

    从上面示意图可以看出调用方法 **flip **的时候会将写入时候回的position指针的值赋给limit同时重置position的值到0的位置。这里就实现了读写的模式转换。如果再次读取的时候就能够将写入到ByteBuffer的值读取出来。

    方法flip主要用于读写模式的切换

    Tips: 如果你调用flip方法后读取的数据或者写入的数据超过了limit会有错误抛出

    3.2 mark-API

    使用代码如下:

    public static void main(String[] args) throws Exception{
    
            ByteBuffer allocate = ByteBuffer.allocate(20); //分配一个大小为20bytes的ByteBuffer
            allocate.putLong(10L);
            allocate.putInt(1);
            allocate.mark();
            System.out.println(allocate.capacity());//20
            System.out.println(allocate.limit());//20
            System.out.println(allocate.position());//12
            System.out.println("-----------------------");
            allocate.getLong();
            System.out.println(allocate.capacity());//20
            System.out.println(allocate.limit());//20
            System.out.println(allocate.position());//20
            allocate.reset();
            System.out.println("-----------------------");
            System.out.println(allocate.capacity());//20
            System.out.println(allocate.limit());//20
            System.out.println(allocate.position());//12
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    示意图如下:

    从上图可以知道调用 **mark 是将position的值赋给mark属性。然后你进行接下来的继续读写操作。当你需要将position恢复到标记字段的时候调用 reset ** 进行恢复。

    Tips: 如果你调用mark然后有调用了flip,flip会将mark进行重置。

    3.3 compact-API

    使用代码实例如下:

    public static void main(String[] args) throws Exception{
    
        ByteBuffer allocate = ByteBuffer.allocate(20); //分配一个大小为20bytes的ByteBuffer
        allocate.putLong(10L);
        allocate.flip();
        allocate.getInt();
        System.out.println(allocate.capacity());//20
        System.out.println(allocate.limit());//8
        System.out.println(allocate.position());//4
        allocate.compact();
        System.out.println("----------------------");
        System.out.println(allocate.capacity());//20
        System.out.println(allocate.limit());//20
        System.out.println(allocate.position());//4
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    示意图如下:

    从上图可以看出来 **compact **的主要作用: 用来清楚掉当前position指针之前的数据然后将指针指向limit的位置同时将整个指针往左移动直到替换掉position左边的数据,与此同时还会将limit的值设置为capacity。

    4. 总结

    ByteBuffer总体使用起来和Netty的ByteBuf对比没有Netty ByteBuf好用。但是对于使用原生的Java NIO的开发来说也是可以的。主要是需要用户自己对读写进行转换等操作,使用起来比较繁琐。但是整个ByteBuffer的实现还是比较简单的。

  • 相关阅读:
    Python 内置函数详解 (3) 进制转换
    java Date
    LeetCode_双指针_中等_61. 旋转链表
    卷积神经网络 语义分割,图像分割神经网络算法
    【JAVAEE框架】Spring 项目构建流程
    并发原理 — CPU原子性指令(一)
    JVM 和 HotSpot 的区别?
    MPP 与 SMP 的区别,终于有人讲明白了【文末送书】
    VS错误代码LNK1168 LNK1168 无法打开 D:\ruanjian\visualstudio\test\03struct\x64\De
    metaRTC Visual Studio编译指南
  • 原文地址:https://blog.csdn.net/m0_70748381/article/details/126929540