JavaNIO中Buffer的基本概念

Buffer是Java NIO中最基本的概念,也是java.nio包中的最基础的类。《Java NIO》书中对Buffer的描述是“a holding tank, or staging area”,而在《Thinking in Java》则把Buffer比作穿梭于矿井和外部之间的,运载矿物的货车。实际上,在Java NIO中的设计更为接近操作系统底层的实际运作,运用Buffer也是为了提高IO效率,这篇我们就来看下Buffer相关的最基本的内容。

0.  Buffer

Buffer是Java API在java.nio包中的第一个类,而java.nio包中的类大部分都是Buffer的子类。Buffer类本身是抽象的,甚至连put()、get()方法都没有给出,它主要有这样一些直接子类,分别和非布尔的基本类型对应: ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer。

再来看JDK中Buffer类的实现,是围绕对应于Buffer的一个数组展开的,Buffer类中给出了如下4个属性变量:

  • mark 在某些条件下,需要特殊标记position位置时会用到
  • position  指向当前位置的数组index
  • limit 指向可以访问的结尾的数组index,默认等同于capacity
  • capacity 指向数组最大位置的index(+1)

另外,这4个属性变量中前后每两个都恒有<=关系。

和这4个属性对应,Buffer中还提供了一些相关的方法:

  • mark() 标记mark位当前position位置
  • reset() 恢复position到mark位置,前提是mark不为默认值-1
  • position()和position(int) 前者获取当前position值,后者设置position值
  • clear() 恢复到最初的状态,即mark为-1,position为0,limit为capacity,为新一轮channel的读操作做准备
  • flip() 为新一轮写操作做准备,和clear()不同的地方是把limit设置为position,而非capacity
  • rewind() 和flip()类似,不同之处在于不会设置limit到position位置

有意思的是,上述方法大部分都是返回Buffer类型的,即Buffer对象本身,可以用来连续做实例方法调用。

此外,Buffer中有array()和arrayOffset()方法,但是是抽象的未实现的方法。

还有address属性,按注释说明,是Direct Buffer才会用到。

1. CharBuffer等其他基本类型对应的Buffer子类

关于Buffer的子类,我们可以看如下图。

Buffer的子类

Buffer的子类

我们看到这些直接子类都有A标记,也就是说它们都是抽象类。

这些类的具体实现都是Java API之外的,往往都和具体的平台底层结构有关,它们封装了如字节序处理相关等内容。但对于上述这些Buffer子类,我们通常不必关心其具体实现。我们只需要通过allocate()和wrap()方法来分配或者初始化即可。

allocate()和wrap()都是静态方法,在Buffer的子类中有实现。其中allocate()是新分配,而wrap()则可以通过实现实例化的数组作为Buffer的数据存储支持。其中ByteBuffer还有allocateDirect()方法,这个后面的文章详细说明。

对应地,子类也都给出了Buffer类中array()和arrayOffset()的具体实现,在Buffer使用堆中的数组支持的并且非只读情况下,会分别返回数组和偏移量。有hasArray()方法判断前两者的可用性。

还有,最基本地,这些Buffer的直接子类也都给出了各种重载的put()和get()方法,但是是抽象的,具体实现在这些Buffer直接子类的具体子类中实现。如用堆中数组作为数据支持的实现中,put()和get()则分别是对数组进行存取操作。

除了上面所说的,这些子类也基本都提供了这样几个方法:

  • compact() 这个方法也与Buffer标记相关的四个基本属性变量有关,通常是在读了一部分数据之后又开始写数据,把未读的数据拷贝至开头段,并设置各指针准备继续写入。
  • compareTo() 这个方法则与具体的类型有关,Buffer的几个子类基本都是先了Comparable接口。
  • duplicate() 复制,生成一个新的Buffer子类对象,在Buffer个子类中是抽象的;通常在堆实现的情况下,使用的仍是同一个支持数据存储的数组。
  • slice() 和上一个类似,只是角标有所调整。

其它的就是个子类结合类型的情况和实现不同的接口提供的方法了,如CharBuffer实现了Appendable接口,有append()方法等。

2. 其它

上面已经对Buffer和Buffer各子类通用的最基本的内容做了阐述。而在JDK实现上不同平台有所不同,这个就不做细节介绍了,可以参见对应平台的JDK源码。

而ByteBuffer是直接对应字节处理的,比较特殊。关于ByteBuffer的特征,后文会详细整理。

作者原创,难免有错误,欢迎读者热心评论留言指出,以免误导他人,谢谢!

相关文章:

此条目发表在 IO, Java, Java语言, 开发, 计算机技术 分类目录,贴了 , , , , 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>