文章目录

  • 简介
  • Buffer是什么
  • Buffer进阶
  • 创建Buffer
  • Direct VS non-Direct
  • Buffer的日常操作
    • 向Buffer写数据
    • 从Buffer读数据
    • rewind Buffer
    • Compact Buffer
    • duplicate Buffer
  • 总结

简介

小师妹在学习NIO的路上越走越远,唯一能够帮到她的就是在她需要的时候给她以全力的支持。什么都不说了,今天介绍的是NIO的基础Buffer。老铁给我上个Buff。

Buffer是什么

小师妹:F师兄,这个Buffer是我们纵横王者峡谷中那句:老铁给我加个Buff的意思吗?

当然不是了,此Buffer非彼Buff,Buffer是NIO的基础,没有Buffer就没有NIO,没有Buffer就没有今天的java。

因为NIO是按Block来读取数据的,这个一个Block就可以看做是一个Buffer。我们在Buffer中存储要读取的数据和要写入的数据,通过Buffer来提高读取和写入的效率。

更多精彩内容且看:

  • 区块链从入门到放弃系列教程-涵盖密码学,超级账本,以太坊,Libra,比特币等持续更新
  • Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
  • Spring 5.X系列教程:满足你对Spring5的一切想象-持续更新
  • java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程

更多内容请访问www.flydean.com

还记得java对象的底层存储单位是什么吗?

小师妹:这个我知道,java对象的底层存储单位是字节Byte。

对,我们看下Buffer的继承图:

Buffer是一个接口,它下面有诸多实现,包括最基本的ByteBuffer和其他的基本类型封装的其他Buffer。

小师妹:F师兄,有ByteBuffer不就够了吗?还要其他的类型Buffer做什么?

小师妹,山珍再好,也有吃腻的时候,偶尔也要换个萝卜白菜啥的,你以为乾隆下江南都干了些啥?

ByteBuffer虽然好用,但是它毕竟是最小的单位,在它之上我们还有Char,int,Double,Short等等基础类型,为了简单起见,我们也给他们都搞一套Buffer。

Buffer进阶

小师妹:F师兄,既然Buffer是这些基础类型的集合,为什么不直接用结合来表示呢?给他们封装成一个对象,好像有点多余。

我们既然在面向对象的世界,从表面来看自然是使用Object比较合乎情理,从底层的本质上看,这些封装的Buffer包含了一些额外的元数据信息,并且还提供了一些意想不到的功能。

上图列出了Buffer中的几个关键的概念,分别是Capacity,Limit,Position和Mark。Buffer底层的本质是数组,我们以ByteBuffer为例,它的底层是:

final byte[] hb;
  • Capacity表示的是该Buffer能够承载元素的最大数目,这个是在Buffer创建初期就设置的,不可以被改变。
  • Limit表示的Buffer中可以被访问的元素个数,也就是说Buffer中存活的元素个数。
  • Position表示的是下一个可以被访问元素的index,可以通过put和get方法进行自动更新。
  • Mark表示的是历史index,当我们调用mark方法的时候,会把设置Mark为当前的position,通过调用reset方法把Mark的值恢复到position中。

创建Buffer

小师妹:F师兄呀,这么多Buffer创建起来是不是很麻烦?有没有什么快捷的使用办法?

一般来说创建Buffer有两种方法,一种叫做allocate,一种叫做wrap。

public void createBuffer(){IntBuffer intBuffer= IntBuffer.allocate(10);log.info("{}",intBuffer);log.info("{}",intBuffer.hasArray());int[] intArray=new int[10];IntBuffer intBuffer2= IntBuffer.wrap(intArray);log.info("{}",intBuffer2);IntBuffer intBuffer3= IntBuffer.wrap(intArray,2,5);log.info("{}",intBuffer3);intBuffer3.clear();log.info("{}",intBuffer3);log.info("{}",intBuffer3.hasArray());}

allocate可以为Buffer分配一个空间,wrap同样为Buffer分配一个空间,不同的是这个空间背后的数组是自定义的,wrap还支持三个参数的方法,后面两个参数分别是offset和length。

INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
INFO com.flydean.BufferUsage - true
INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=2 lim=7 cap=10]
INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
INFO com.flydean.BufferUsage - true

hasArray用来判断该Buffer的底层是不是数组实现的,可以看到,不管是wrap还是allocate,其底层都是数组。

需要注意的一点,最后,我们调用了clear方法,clear方法调用之后,我们发现Buffer的position和limit都被重置了。这说明wrap的三个参数方法设定的只是初始值,可以被重置。

Direct VS non-Direct

小师妹:F师兄,你说了两种创建Buffer的方法,但是两种Buffer的后台都是数组,难道还有非数组的Buffer吗?

自然是有的,但是只有ByteBuffer有。ByteBuffer有一个allocateDirect方法,可以分配Direct Buffer。

小师妹:Direct和非Direct有什么区别呢?

Direct Buffer就是说,不需要在用户空间再复制拷贝一份数据,直接在虚拟地址映射空间中进行操作。这叫Direct。这样做的好处就是快。缺点就是在分配和销毁的时候会占用更多的资源,并且因为Direct Buffer不在用户空间之内,所以也不受垃圾回收机制的管辖。

所以通常来说只有在数据量比较大,生命周期比较长的数据来使用Direct Buffer。

看下代码:

public void createByteBuffer() throws IOException {ByteBuffer byteBuffer= ByteBuffer.allocateDirect(10);log.info("{}",byteBuffer);log.info("{}",byteBuffer.hasArray());log.info("{}",byteBuffer.isDirect());try (RandomAccessFile aFile = new RandomAccessFile("src/main/resources/www.flydean.com", "r");FileChannel inChannel = aFile.getChannel()) {MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());log.info("{}",buffer);log.info("{}",buffer.hasArray());log.info("{}",buffer.isDirect());}}

除了allocateDirect,使用FileChannel的map方法也可以得到一个Direct的MappedByteBuffer。

上面的例子输出结果:

INFO com.flydean.BufferUsage - java.nio.DirectByteBuffer[pos=0 lim=10 cap=10]
INFO com.flydean.BufferUsage - false
INFO com.flydean.BufferUsage - true
INFO com.flydean.BufferUsage - java.nio.DirectByteBufferR[pos=0 lim=0 cap=0]
INFO com.flydean.BufferUsage - false
INFO com.flydean.BufferUsage - true

Buffer的日常操作

小师妹:F师兄,看起来Buffer确实有那么一点复杂,那么Buffer都有哪些操作呢?

Buffer的操作有很多,下面我们一一来讲解。

向Buffer写数据

向Buffer写数据可以调用Buffer的put方法:

public void putBuffer(){IntBuffer intBuffer= IntBuffer.allocate(10);intBuffer.put(1).put(2).put(3);log.info("{}",intBuffer.array());intBuffer.put(0,4);log.info("{}",intBuffer.array());}

因为put方法返回的还是一个IntBuffer类,所以Buffer的put方法可以像Stream那样连写。

同时,我们还可以指定put在什么位置。上面的代码输出:

INFO com.flydean.BufferUsage - [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
INFO com.flydean.BufferUsage - [4, 2, 3, 0, 0, 0, 0, 0, 0, 0]

从Buffer读数据

读数据使用get方法,但是在get方法之前我们需要调用flip方法。

flip方法是做什么用的呢?上面讲到Buffer有个position和limit字段,position会随着get或者put的方法自动指向后面一个元素,而limit表示的是该Buffer中有多少可用元素。

如果我们要读取Buffer的值则会从positon开始到limit结束:

public void getBuffer(){IntBuffer intBuffer= IntBuffer.allocate(10);intBuffer.put(1).put(2).put(3);intBuffer.flip();while (intBuffer.hasRemaining()) {log.info("{}",intBuffer.get());}intBuffer.clear();}

可以通过hasRemaining来判断是否还有下一个元素。通过调用clear来清除Buffer,以供下次使用。

rewind Buffer

rewind和flip很类似,不同之处在于rewind不会改变limit的值,只会将position重置为0。

public void rewindBuffer(){IntBuffer intBuffer= IntBuffer.allocate(10);intBuffer.put(1).put(2).put(3);log.info("{}",intBuffer);intBuffer.rewind();log.info("{}",intBuffer);}

上面的结果输出:

INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]

Compact Buffer

Buffer还有一个compact方法,顾名思义compact就是压缩的意思,就是把Buffer从当前position到limit的值赋值到position为0的位置:

public void useCompact(){IntBuffer intBuffer= IntBuffer.allocate(10);intBuffer.put(1).put(2).put(3);intBuffer.flip();log.info("{}",intBuffer);intBuffer.get();intBuffer.compact();log.info("{}",intBuffer);log.info("{}",intBuffer.array());}

上面代码输出:

INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=0 lim=3 cap=10]
INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=2 lim=10 cap=10]
INFO com.flydean.BufferUsage - [2, 3, 3, 0, 0, 0, 0, 0, 0, 0]

duplicate Buffer

最后我们讲一下复制Buffer,有三种方法,duplicate,asReadOnlyBuffer,和slice。

duplicate就是拷贝原Buffer的position,limit和mark,它和原Buffer是共享原始数据的。所以修改了duplicate之后的Buffer也会同时修改原Buffer。

如果用asReadOnlyBuffer就不允许拷贝之后的Buffer进行修改。

slice也是readOnly的,不过它拷贝的是从原Buffer的position到limit-position之间的部分。

public void duplicateBuffer(){IntBuffer intBuffer= IntBuffer.allocate(10);intBuffer.put(1).put(2).put(3);log.info("{}",intBuffer);IntBuffer duplicateBuffer=intBuffer.duplicate();log.info("{}",duplicateBuffer);IntBuffer readOnlyBuffer=intBuffer.asReadOnlyBuffer();log.info("{}",readOnlyBuffer);IntBuffer sliceBuffer=intBuffer.slice();log.info("{}",sliceBuffer);}

输出结果:

INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
INFO com.flydean.BufferUsage - java.nio.HeapIntBufferR[pos=3 lim=10 cap=10]
INFO com.flydean.BufferUsage - java.nio.HeapIntBuffer[pos=0 lim=7 cap=7]

总结

今天给小师妹介绍了Buffer的原理和基本操作。

本文的例子https://github.com/ddean2009/learn-java-io-nio

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/java-io-nio-buffer/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

小师妹学JavaIO之:Buffer和Buff相关推荐

  1. java buff_小师妹学JavaIO之:Buffer和Buff

    简介 小师妹在学习NIO的路上越走越远,唯一能够帮到她的就是在她需要的时候给她以全力的支持.什么都不说了,今天介绍的是NIO的基础Buffer.老铁给我上个Buff. Buffer是什么 小师妹:F师 ...

  2. 小师妹学JavaIO之:NIO中那些奇怪的Buffer

    文章目录 简介 Buffer的分类 Big Endian 和 Little Endian aligned内存对齐 总结 简介 妖魔鬼怪快快显形,今天F师兄帮助小师妹来斩妖除魔啦,什么BufferB,B ...

  3. 小师妹学JavaIO之:用Selector来发好人卡

    文章目录 简介 Selector介绍 创建Selector 注册Selector到Channel中 SelectionKey selector 和 SelectionKey 总的例子 总结 简介 NI ...

  4. 小师妹学JavaIO之:MappedByteBuffer多大的文件我都装得下

    文章目录 简介 虚拟地址空间 详解MappedByteBuffer MapMode MappedByteBuffer的最大值 MappedByteBuffer的使用 MappedByteBuffer要 ...

  5. 小师妹学JavaIO之:NIO中Channel的妙用

    文章目录 简介 Channel的分类 FileChannel Selector和Channel DatagramChannel SocketChannel ServerSocketChannel As ...

  6. 小师妹学JavaIO之:文件写入那些事

    文章目录 简介 字符输出和字节输出 格式化输出 输出其他对象 在特定的位置写入 给文件加锁 总结 简介 小师妹又对F师兄提了一大堆奇奇怪怪的需求,要格式化输出,要特定的编码输出,要自己定位输出,什么? ...

  7. 小师妹学JavaIO之:File copy和File filter

    文章目录 简介 使用java拷贝文件 使用File filter 总结 简介 一个linux命令的事情,小师妹非要让我教她怎么用java来实现,哎,摊上个这么杠精的小师妹,我也是深感无力,做一个师兄真 ...

  8. 小师妹学JavaIO之:try with和它的底层原理

    文章目录 简介 IO关闭的问题 使用try with resource try with resource的原理 自定义resource 总结 简介 小师妹是个java初学者,最近正在学习使用java ...

  9. mappedbytebuffer_小师妹学JavaIO之:MappedByteBuffer多大的文件我都装得下

    简介 大大大,我要大!小师妹要读取的文件越来越大,该怎么帮帮她,让程序在性能和速度上面得到平衡呢?快来跟F师兄一起看看吧. 虚拟地址空间 小师妹:F师兄,你有没有发现,最近硬盘的价格真的是好便宜好便宜 ...

最新文章

  1. 使用MakeCAB.exe命令创建CAB文件
  2. 2021年春季学期-信号与系统-第九次作业参考答案-第六小题
  3. C++Primer Plus (第六版)阅读笔记 + 源码分析【目录汇总】
  4. Python环境下的数据库编程
  5. 虚拟专题:联邦学习 | 联邦学习算法综述
  6. ceph升级到10.2.3 版本启动服务报错:Unknown lvalue 'TasksMax' in section 'Service'
  7. android 底部导航总结
  8. OC开发实例变量的访问控制详解
  9. 显示隐藏dataGrid控件的标题栏,改变标题栏高度.
  10. sqlloader 直接路径和常规路径_sqlloader
  11. 微软洪小文全面解读智能层级:目前的智能都是AI+HI
  12. 华为+android+root权限获取root,华为手机root权限获取方法
  13. node静态服务器tudo
  14. 视频基本原理 -视频接口之 - MIPI接口(五)
  15. Qt引入图标字体包iconfont
  16. 51单片机完成时钟形式显示
  17. EC200U open方案环境搭建
  18. 怎么样可以调用阿里云短信服务接口实现短信验证码
  19. GO在windows环境下的下载以及环境配置
  20. map集合---------今日份下饭菜,妈妈再也不用担心我饿肚子了,学好List, Set,与Map,装的下,世界就是你的

热门文章

  1. 1039. 到底买不买(20)
  2. “白加黑”远控木马技术分析及手杀方案
  3. Kafka设计解析(四):Kafka Consumer解析
  4. cocos2d-x初探学习笔记(2)--重要概念及Test例子结构
  5. 网络优化实践探索文章
  6. 轻松掌控全链路监控:方案概述与对比 | 真的很干!
  7. 曹大带我学 Go(7)—— 如何优雅地指定配置项
  8. 从 wiscKey 看 LSMtree 的不足
  9. 【线上分享】视频质量评价:挑战与机遇
  10. Google发布Objectron数据集