ByteBuffer使用揭秘

  • 结构介绍
  • 常用方法说明
    • ByteBuffer.remaining()
    • ByteBuffer.hasRemaining()
    • ByteBuffer.flip()
    • ByteBuffer.clear()
    • ByteBuffer.rewind()
    • ByteBuffer.mark()与ByteBuffer.reset()
  • 实操演示
    • 场景1:先写后读
    • 场景2:写→标记→写→重置标记→写→读
    • 场景3:写→标记→写→重置标记→读
    • 场景4:写→读→判断数据是否完全读取→没读完继续读
    • 场景5:写→重复读
  • 总结

结构介绍

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指针到0allocate.rewind();
}输出:
Hello World!
Hello World!
Hello World!

总结

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

ByteBuffer使用揭秘相关推荐

  1. 闫燕飞:Kafka的高性能揭秘及优化

    大家下午好,我是来自腾讯云基础架构部ckafka团队的高级工程师闫燕飞.今天在这里首先为大家先分享一下开源Kafka在高性能上面的一些关键点,然后我会分享一下我们腾讯云ckafka对社区Kafka所做 ...

  2. 康复治疗学可以考计算机吗,【大揭秘】2018“人机对话”康复医学治疗技术专业技术资格考试...

    原标题:[大揭秘]2018"人机对话"康复医学治疗技术专业技术资格考试 昨天,关于"2018年康复医学治疗技术专业技术资格考试采用人机对话考试方式"的通知一经发 ...

  3. 零代价修复海量服务器的内核缺陷——UCloud内核热补丁技术揭秘

    下述为UCloud资深工程师邱模炯在InfoQ架构师峰会上的演讲--<UCloud云平台的内核实践>中非常受关注的内核热补丁技术的一部分.给大家揭开了UCloud云平台内核技术的神秘面纱. ...

  4. 红芯丑闻揭秘者 Touko 专访 | 关于红芯丑闻的更多内幕……

    专栏 | 九章算法 网址 | www.jiuzhang.com ❤ 红芯事件 近日,一则<自主研发的国产浏览器内核,红芯宣布获2.5亿C轮融资>的讯息再次将"国产自主创新&quo ...

  5. 揭秘vue——vue-cli3全面配置

    ★ vue-cli3 全面配置 ★ Nuxt.js 全面配置 创建项目 配置环境变量   通过在package.json里的scripts配置项中添加--mode xxx来选择不同环境   在项目根目 ...

  6. 【云周刊】第146期:史上最大规模人机协同的双11,12位技术大V揭秘背后黑科技...

    摘要: 史上最大规模人机协同的双11,12位技术大V揭秘背后黑科技,INTERSPEECH 2017系列 | 语音识别之语言模型技术,机器学习初学者必须知道的十大算法,云数据库SQL Server 2 ...

  7. 男人约会动机大揭秘。

    被人约是好事,但也要眼明心亮,男人约会动机大揭秘. 男人约会动机大揭秘: 1:这是一个有诚意的开始,表明他是早有计划和你约会的.至少,也说明他有相当的社交礼貌. 2:这个男人可能只是一时寂寞了,如果你 ...

  8. 3dmax图像采样器抗锯齿_内幕揭秘!同样的场景同一张图,用3DMAX网渲平台进行二次渲染时间竟然相差3个小时之多!...

    一个分辨率:4000*2000的室内客餐厅,3dmax版本是2014版本,渲染器版本为vray3.63,机器:阿里云1台服务器,这个同样的场景同样的参数同一张图,用3dmax网渲平台进行二次渲染发现时 ...

  9. EIGRP OSFP 利用NULL0接口防止路由环路 Loopback Null0接口揭秘

    在EIGRP中,只要发生总结就会在路由表中自动产生一条指向NULL0的路由条目,这条路由的直接意思是:匹配这条路由的数据包会被路由器丢掉.它的目的是为了避免在某些情况下产生路由环路. 以第三四学期的中 ...

最新文章

  1. FPGA逻辑设计回顾(11)FPGA以及PC中的RAM与ROM
  2. 为什么叫python编程-Python这么火,为什么说它不是未来的编程语言?
  3. python下载文件并改名_第46p,8行代码,用Python批量重命名文件
  4. shell读取文件到变量、管道重定向、if和while嵌套使用、命令替换
  5. API函数MessageBox的参数与返回值
  6. python列表数据运算_Python基础(2)——数据类型:Numbers、列表、元组、字典、字符串、数据运算...
  7. maven项目动态替换配置中的值
  8. 贵州:2018经济增速继续领先 2019“九字真言”主攻高质量
  9. Excel to SQL(2)
  10. python小白从哪来开始-写给小白的工程师入门 - 从 Python 开始
  11. java zip 文件夹_Java Zip文件文件夹示例
  12. mockito 外部接口_原创 |使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (五)第一个单元测试...
  13. redis安装----非基于lnmp安装
  14. 分布式概念及相关的名词定义
  15. Linux内核数据结构之哈希表
  16. TKMybatis的使用大全和例子(example、Criteria、and、or)
  17. 【视频】极值理论EVT与R语言应用:GPD模型火灾损失分布分析
  18. pip换源-pip国内源
  19. 【MOSBUS-RTU协议】从机地址分配
  20. python中登录基金账户获取基金数据

热门文章

  1. Python的pyecharts做旭日图,太6了
  2. 百度CEO李彦宏在毕业二十周年之际受邀重回母校
  3. Barcode Reader SDK5.xCrack,条形码识别支持多种文档和图像格式
  4. iOS App 安装包瘦身指南
  5. Gunner(map)
  6. 三种样式的九九乘法表—C语言
  7. 《视频解密》中文版(第四版) 第七章 数字视频处理(第二部分)
  8. 王者荣耀改名神器助手微信小程序
  9. 百度大脑UNIT升级,五步完成对话模型私有化部署
  10. 开源项目推荐:使用Qt编写和开发的开源IDE