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的子类
我们看到这些直接子类都有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的特征,后文会详细整理。