2019独角兽企业重金招聘Python工程师标准>>>

ByteBuffer类是在Java NIO中常常使用的一个缓冲区类,使用它可以进行高效的IO操作,但是,如果对常用方法的理解有错误,那么就会出现意想不到的bug。

ByteBuffer类的常用方法
先来看看一个基本的程序

public void test() throws IOException
    {
        ByteBuffer buff = ByteBuffer.allocate(128);
        FileChannel fin = null;
        FileChannel fout = null;
        try
        {
            fin = new FileInputStream("filein").getChannel();
            fout = new FileOutputStream("fileout").getChannel();
            while(fin.read(buff) != -1) {
                buff.flip();
                fout.write(buff);
                buff.clear();
            }
        }
        catch (FileNotFoundException e)
        {
 
        } finally {
            try {
                if(fin != null) {
                    fin.close();
                }
                if(fout != null) {
                    fout.close();
                }
            } catch(IOException e) {
                throw e;
            }
        }
    }
在test方法中,首先通过ByteBuffer.allocate()方法分配了一段内存空间,作为缓存,allocate方法对缓存自动清零,然后打开一个输入文件管道fin和一个输出文件管道fout,在循环中先从fin读出数据存放到buff缓冲区中,再将buff缓冲中的内容写入fout。当然这对于先从文件中读,然后再写这样的场景,这不是高效的做法。 
可以看到先从fin中读出数据后,首先要调用ByteBuffer.flip()方法,若将数据写入输出文件后,还要调用ByteBuffer.clear()方法,为什么要这样做呢?

ByteBuffer可以作为一个缓冲区,是因为它是内存中的一段连续的空间,在ByteBuffer对象内部定义了四个索引,分别是mark,position,limit,capacity,其中

mark用于对当前position的标记

position表示当前可读写的指针,如果是向ByteBuffer对象中写入一个字节,那么就会向position所指向的地址写入这个字节,如果是从ByteBuffer读出一个字节,那么就会读出position所指向的地址读出这个字节,读写完成后,position加1

limit是可以读写的边界,当position到达limit时,就表示将ByteBuffer中的内容读完,或者将ByteBuffer写满了。

capacity是这个ByteBuffer的容量,上面的程序中调用ByteBuffer.allocate(128)就表示创建了一个容量为capacity字节的ByteBuffer对象。

了解了这四个变量之后,再来看看前面的程序。之所以调用ByteBuffer.flip()方法是因为在向ByteBuffer写入数据后,position为缓冲区中刚刚读入的数据的最后一个字节的位置,flip方法将limit值置为position值,position置0,这样在调用get*()方法从ByteBuffer中取数据时就可以取到ByteBuffer中的有效数据,JDK中flip方法的代码如下:

public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}
在调用four.write(buff)时,就将buff缓冲区中的数据写入到输出管道,此时调用ByteBuffer.clear()方法为下次从管道中读取数据做准备,但是调用clear方法并不将缓冲区的数据清空,而是设置position,mark,limit这三个变量的值,JDK中clear方法的代码如下:

public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}
这个方法命名给人的感觉就是将数据清空了,但是实际上却不是的,它并没有清空缓冲区中的数据,而至重置了对象中的三个索引值,如果不清空的话,假设此次该ByteBuffer中的数据是满的,下次读取的数据不足以填满缓冲区,那么就会存在上一次已经处理的的数据,所以在判断缓冲区中是否还有可用数据时,使用ByteBuffer.hasRemaining()方法,在JDK中,这个方法的代码如下:

public final boolean hasRemaining() {
    return position < limit;
}
在该方法中,比较了position和limit的值,用以判断是否还有可用数据。

在ByteBuffer类中,还有个方法是compact,对于ByteBuffer,其子类HeapByteBuffer的compact方法实现是这样的:

public ByteBuffer compact() {
    System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
    position(remaining());
    limit(capacity());
    return this;
}
如果position()方法返回当前缓冲区中的position值,remaining()方法返回limit与position这段区间的长度,JDK中的remaining()方法代码如下

public final int remaining() {
    return limit - position;
}
所以compact()方法中第一条语句作用是将数组hb当前position所指向的位置开始复制长度为limit-position的数据到hb数组的开始出,其中使用到了ix()函数,这个函数是将参数值加上一个offset值,offset即一个偏移值,在这样的比如一个这样的场景对于一个很大的缓冲区,将其分成两段,第一段的起始位置是p1,长度是q1,第二段起始位置是p2,长度是q2,那么可以分别将这两段包装成一个HeapByteBuffer对象,然后这两个HeapByteBuffer对象(ByteBuffer的子类,默认实现)的offset属性分别设置为p1和p2,这样就可以通过在内部使用ix()函数来简化ByteBuffer对外提供的接口,在使用者看来,与默认的ByteBuffer并没有区别。

在compact函数中,接着将当前的缓冲区的position索引置为limit-position,limit索引置为缓冲区的容量,这样调用compact方法中就可以将缓冲区的有效数据全部移到缓冲区的首部,而position指向下一个可写位置。

比如刚刚创建一个ByteBuffer对象buff时,position=0,limit=capacity,那么此时调用buff.hasRemaining()则会返回true,这样来判断缓冲区中是否有数据是不行的,因为此时缓冲区中的存储的全部是0,但是调用一次compact()方法就可以将position置为limit值,这样再通过buff.hasRemaining()就会返回false,可以与后面的逻辑一起处理了。

ByteBuffer还有一个名为mark的方法,该方法设置mark索引为position的值,JDK中的代码如下:

public final Buffer mark() {
    mark = position;
    return this;
}
与其功能相反的方法为reset方法,即将position的值设置为mark,JDK中的代码如下:

public final Buffer reset() {
    int m = mark;
    if (m < 0)
        throw new InvalidMarkException();
    position = m;
    return this;
}
此外还有一个名为rewind的方法,这个方法将position索引置为0,mark索引置为-1,JDK中的代码如下:

public final Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
}
通过这些方法,就可以很方便的操作一个缓冲区,关键是要理解这些方法具体的作用,以及对三个索引值的影响(capacity是不变的)。

ByteBuffer继承自Buffer类,上面的方法四个索引值都定义在Buffer类中,操作索引值的方法也都定义在Buffer类中。

转载于:https://my.oschina.net/u/3872240/blog/3049820

深入理解ByteBuffer相关推荐

  1. 理解 ByteBuffer

    理解 ByteBuffer ByteBuffer 译为 字节缓冲区 , 是 Java nio 包下提供的一个抽象类 java.nio.ByteBuffer 缓冲区即预先分配的内存,是从内存中提前划分出 ...

  2. java ByteBuffer flip()和limit()的理解

    先列点代码片段: // ... // // 此段代码功能为从 t.txt 里复制所有数据到 out_j.txt: // ... 1 FileChannel fcin = new FileInputSt ...

  3. 浅谈IO及不同的理解

    IO 什么是IO? 它是指计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的.单独的程序一般是让系统为它们完 ...

  4. ByteBuffer详解

    2019独角兽企业重金招聘Python工程师标准>>> 在Java nio中,主要有三大组件:Buffer,Channel和Selector.这三者之间的关系可以按照如下方式进行理解 ...

  5. BIO与NIO、AIO的区别(这个容易理解)

    IO的方式通常分为几种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO. 一.BIO 在 JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSoc ...

  6. 面试:说说你对“零拷贝”的理解?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://my.oschina.net/ ...

  7. Java NIO理解与使用

    2019独角兽企业重金招聘Python工程师标准>>> Netty的使用或许我们看着官网user guide还是很容易入门的.因为Java nio使用非常的繁琐,netty对Java ...

  8. (转)zookeeper理解

    分布式服务框架 Zookeeper -- 管理分布式环境中的数据 Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题 ...

  9. 我所理解的Java NIO

    这两天了解了一下关于NIO方面的知识,网上关于这一块的介绍只是介绍了一下基本用法,没有系统的解释NIO与阻塞.非阻塞.同步.异步之间的联系,导致自己困扰了好久.本篇文章就个人关于NIO的理解进行阐述. ...

最新文章

  1. 如何理解Memory leak
  2. JEECG支付宝服务窗开发培训视频
  3. CRUX下实现进程隐藏(3)
  4. 那么多GAN哪个好?谷歌大脑泼来冷水:都和原版差不多
  5. JSONObject.fromObject--JSON与对象的转换
  6. java web 初始化方法_Java Web(二) 类的初始化及初始化顺序
  7. Spring BPP中优雅的创建动态代理Bean
  8. Unity基础知识—Transform
  9. GoEasy小程序即时通讯源码 v1.1.0/基于GoEasy提供的websocket通讯服务
  10. kindle中azw3和mobi哪个好?
  11. 硬见小百科:尺寸公差、形位公差、表面粗糙度数值上的关系
  12. 什么是阿里云服务器ECS?
  13. 消息推送实现方法、移动终端及消息推送系统
  14. 软件测试工程师,不只是你眼中的点点点
  15. matlab2010 notebook,Matlab在Win10 64位下用notebook的问题
  16. MDN-CSS-排版社区大学首页
  17. 偶感 - 写在细雨朦胧的早晨
  18. php 抓站,如何跨站抓取别的站点的页面的补充
  19. flask 调用python脚本_flaskpython脚本如何调用另一个flaskpython脚本
  20. 未来十年万亿美元的云“风口”,亚马逊云科技再次加速“起飞”

热门文章

  1. 梭子鱼任命James Forbes-May为亚太区销售副总裁
  2. putty修改字体配色
  3. 有关rsync的一些语句
  4. IE6、 IE7、IE8、Firefox兼容性问题
  5. [转载翻译][重新整理]西川善司的”METAL GEAR SOLID 4”图形讲座(5)
  6. java awt button_AWT Button类
  7. python pygame模块按键延迟_如何在python/pygame中延迟一件事情而不延迟其他事情?...
  8. python找不到tushare_python tushare
  9. 单片机控制24v电压_最全变频器控制端子接线方法和技巧
  10. aac fhg lc哪一个模式_电流模式变换器输出LC滤波器对反馈环影响