ByteBuffer使用揭秘

结构介绍

ByteBuffer是NIO产生的时候带出来的一个对象,是Channel读写数据的缓冲区

在这里插入图片描述

我们在使用NIO的时候通常会看到这样一行代码:

 ByteBuffer allocate = ByteBuffer.allocate(1024);

它底层的实现是这样的,实际就是上图所标记的HeapByteBuffer

在这里插入图片描述

底层其实就是一个字节数组,但是有几个不同作用的指针,再我们执行完上面那行代码后,初始化的结构如下图所示:

在这里插入图片描述

生成一个长度为1024的数组以及4个指针,分别是:

  • position: 读写指针,在写的情况下,记录写数据的偏移量;在读的情况下,记录读数据的偏移量;
  • capacity: 数组大小的边界(就是数组容量,不会变)
  • limit: 读写边界指针,写数据的边界或者读数据的边界
  • mark: 标记指针,可以标记一个position的位置

重点:写或者读的空间都是 position——limit之间的空间

这样一看可能有点懵逼是吧,我们换个思路想,这个缓冲区分为读、写两种模式,像上图初始化后默认就是写模式,所以position到limit之间都是可写的空间(1024),每写入一个字节,position就会像前移动一个字节,假设我们写入 “Hello”,一共五个字节,那内部就是这样的:

此时:position变为了5
可写空间为:position——limit 之间  可以继续写,position会一直向前移动
可读空间为:position——limit 之间  虽然可以读,但是读取不到数据
这就可以称为写模式

在这里插入图片描述

上图还是写模式,如果想读取数据,那就需要切换到读模式,切换后结构如下:

此时:position变为了0 ,limit变为了5
可写空间为:position——limit 之间  可以写,但是会覆盖之前内容且不得超过limit
可读空间为:position——limit 之间  所以可以读取数据,每读一个字节,position向前移动一个字节
这就被称为读模式

在这里插入图片描述

好了,到这相信大家有了一个大概的了解了

常用方法说明

ByteBuffer.remaining()

获取position—limit之间还有多少空间,也就是返回两者差值

常用来获取可读数据的长度,或者可写数据的空间大小

源代码如下:

在这里插入图片描述

ByteBuffer.hasRemaining()

判断是否还可读 或者 是否还有空间可写 true:可写或未读完 false:反之

常用来判断数据是否完全读取、是否还能写入数据

源代码如下:

在这里插入图片描述

ByteBuffer.flip()

从写模式切换为读模式,实际就是移动指针

在写模式下,position指针会一直向前移动,但是我们想读取数据是在0——position之间的
所以我们需要将limit放到position的位置,position置为0,此时再读position-limit就是我们想要的数据啦
所以此操作可以称为写模式到读模式的切换

源代码如下:

在这里插入图片描述

ByteBuffer.clear()

重置指针,将指针恢复到初始化的情况

恢复到初始化的指针情况,目的是让你可以重新的写入
此时注意,此操作只是重置了指针并没有清除数据
如果缓冲区内有数据,重置了后去读还是可以读取数据的

源代码如下:

在这里插入图片描述

ByteBuffer.rewind()

重置position指针

这个重置与上面的重置不同,只重置了position指针,有两层语义,同样不会清除数据
写模式下:可以让你重新写
读模式下:可以让你重新读

而clear则是强制恢复到初始位置

源代码如下:

在这里插入图片描述

ByteBuffer.mark()与ByteBuffer.reset()

是不是感觉mark指针没用到?这个只是个标记指针

ByteBuffer.mark()源代码如下:

在这里插入图片描述

ByteBuffer.reset()源代码如下:

在这里插入图片描述

这两个方法一般搭配使用,场景就是position指针读取或者写入都会不断向前,要是中间有段内容想要重写怎么办?中间有段内容想要重新读取怎么办?所以我们打上一个标记点,之后方便重新回到这个标记点重新操作

  • mark():记录当前position的位置
  • reset(): 将position恢复到mark指针标记的位置

接着上面写入的图来,Mark后是这样的:

reset后不会清除数据,图示只是为了方便

在这里插入图片描述

get和put这种简单的就不说明了

实操演示

场景1:先写后读

// 初始化
ByteBuffer allocate = ByteBuffer.allocate(1024);
// 写入数据
allocate.put("Hello".getBytes());
// 切换指针 然后读取
allocate.flip();
// 初始化一个和可读数据一样大小的数组
byte[] bytes = new byte[allocate.remaining()];
// 将数据读取到数组中
allocate.get(bytes);
System.out.println(new String(bytes));

输出结果:Hello

场景2:写→标记→写→重置标记→写→读

// 初始化
ByteBuffer allocate = ByteBuffer.allocate(1024);
// 写入数据
allocate.put("Hello".getBytes());
// 标记
allocate.mark();
// 写入数据
allocate.put(" Hello".getBytes());
// 重置到标记点
allocate.reset();
// 再重新写,覆盖之前的错误数据
allocate.put(" World!".getBytes());
// 切换指针 然后读取
allocate.flip();
// 初始化一个和可读数据一样大小的数组
byte[] bytes = new byte[allocate.remaining()];
// 将数据读取到数组中
allocate.get(bytes);
System.out.println(new String(bytes));

输出:Hello World!

场景3:写→标记→写→重置标记→读

// 初始化
ByteBuffer allocate = ByteBuffer.allocate(1024);
// 写入数据
allocate.put("Hello".getBytes());
// 标记
allocate.mark();
// 写入数据
allocate.put(" World!".getBytes());
// 重置到标记点
allocate.reset();
// 切换指针 然后读取
allocate.flip();
// 初始化一个和可读数据一样大小的数组
byte[] bytes = new byte[allocate.remaining()];
// 将数据读取到数组中
allocate.get(bytes);
System.out.println(new String(bytes));

输出:Hello 因为重置到标记点了,标记点后的数据就读取不了

场景4:写→读→判断数据是否完全读取→没读完继续读

// 初始化
ByteBuffer allocate = ByteBuffer.allocate(1024);
// 写入数据
allocate.put("Hello World!".getBytes());
// 切换指针 然后读取
allocate.flip();
// 初始化一个和可读数据一样大小的数组
byte[] bytes = new byte[5];
// 将数据读取到数组中
allocate.get(bytes);

String data=new String(bytes);
// 才读了5个字节肯定没读完
while (allocate.hasRemaining()){
    System.out.println("没读完继续读");
    byte[] bytes1 = new byte[allocate.remaining()];
    allocate.get(bytes1);
    data+=new String(bytes1);
}
System.out.println(data);

输出:
没读完继续读
Hello World!

场景5:写→重复读

// 初始化
ByteBuffer allocate = ByteBuffer.allocate(1024);
// 写入数据
allocate.put("Hello World!".getBytes());
// 切换指针 然后读取
allocate.flip();
// 初始化一个和可读数据一样大小的数组
byte[] bytes = new byte[allocate.remaining()];
//重复读三次
for (int i = 0; i < 3; i++) {
    // 将数据读取到数组中
    allocate.get(bytes);
    System.out.println(new String(bytes));
    // 重置position指针到0
    allocate.rewind();
}

输出:
Hello World!
Hello World!
Hello World!

总结

可以看到ByteBuffer底层结构和逻辑都是比较简单的,所有的操作也都是和指针有关,只要把指针这块的逻辑搞清楚了,就算使用时忘记了,直接看一下源码也就OK了

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值