Java NIO笔记 ByteBuffer
最近在做一个网络相关的应用,考虑到效率问题,参考了NIO库的实现。NIO库大概更接近C/C++的Socket编程,采用非阻塞的设计,以及Selector事件驱动的设计,感觉还是不错的实现。以下内容只是整理作为参考,可能有不少错误,希望对大家有帮助。如果有更好的实现方法,或者我理解上的一些问题,希望指出,共同学习。
ByteBuffer类(java.nio.ByteBuffer)的用法还真的有点迷糊,似乎它只用于NIO中。它不像StringBuffer那样定位明确。ByteBuffer没有定向为原始类型的一些比较友好的函数。而是派生出多种Buffer,而每个Buffer本身只是容器,而一个Buffer就可以看作是数组。(从实现上看,实际上是自身实现定位的数组,定位的实现就是依靠三个标记)
理解ByteBuffer对象,必须理解其中的三个属性:position、limit、capacity(这是为了表述方便而说的属性,实际上是三个函数,不过内部实现实际上就是一些标记而已)。这三个属性的设置和Java Bean规范不太一样,直接用属性名为函数,有参数的即setter,无参的是getter:
- capacity是总的容量,每个Buffer是一个定长的数组,如果存放的数据超过这个值,就会发生溢出异常。
- position是当前游标,标识当前数据位置。是确定当前是否可写数据的标志之一。
- limit是当前大小。用于确定剩余数据量的标尺之一。
具体例子:
在对某个Buffer进行读取的时候,应该先确定开始游标position,然后,每次调用get()(及getXX()系列函数)的时候游标position会递增(递增单位由getXXX()系列函数决定)。
而limit则是用在计算hasRemaining()函数里,用于确定是否还有数据可读。所以,limit的设置,要视情况而定。也就是说,要读取的数据,就是在Buffer里的position到limit里面的数据。
// write to the buffer. ByteBuffer dst = ByteBuffer.allocate(n); dst.put(...); // read from the buffer. dst.flip(); while (dst.hasRemaining()) { System.out.println((char) dst.get()); }
逐个方法的笔记:
hasRemaining()方法,这个方法的作用是测量当前剩余数据,实际上就是position到limit之间的数组内容。因此,从这个方法看,position和limit属性的定义就很明确了,limit是当前读的末下标,而position是当前读着的下标。
flip()方法的作用是为把当前读的数据作准备输出。具体实现是先设置limit为当前游标position,然后position设置为0,如果用hasRemaining()来确定读取结束标志,那么,读取的内容就是当前读进Buffer的所有内容了。
rewind()方法的作用就是把当前游标归零。而实现是仅仅设置游标position为0,并没有设置limit,那么limit的值是不定的。
reset()/mark()方法的作用是复位/记录上一次存储的状态(不包括数据)。reset()调用前必须调用过mark()方法保存当前状态,否则会发生异常。
clear()方法的作用是清除当前游标position为0,而limit为最大值capacity。但并不会清除数据!
put()系列方法,则是根据类型,往ByteBuffer类对象中放置数据。注意的是,limit的值肯定比游标position大,但不一定就是当前的position的值或者最大值,因此,如果依靠put()系列方法进行存储后,直接调用rewind()方法来读取数据,读取的数据将是不确定的。读取应该调用flip()方法来确定当前limit或者手工确定要读取的范围。
其实最让人迷惑的可能就是rewind()、flip()、reset()、clear()四个方法了。
ByteBuffer是可重用的,不过要注意重用的方法。一般调用clear()丢弃当前数据以进行写操作(实际上,丢弃当前数据可以直接把position设置为0,但要注意的是limit属性),而flip()则是初始化读操作。至于特殊情况,就自己考虑position和limit的位置即可。
Jun 24, 2012 12:10:20 AM
看了一遍之后,对flip()方法很没有感觉@x@
Jun 24, 2012 08:44:44 AM
@兔子: 已修改。
Jun 25, 2012 06:46:33 PM
@Plux: (⊙v⊙)嗯