最近项目中折腾到Java的新I/O接口,不得不说这个NIO的实现思路还是很不错的,相比传统的阻塞式I/O,NIO具有非阻塞,高效率。通过抽象出通道,缓冲器的概念来与数据打交道,减少了很多步骤,使用更为便捷。NIO支持文件I/O和网络I/O,高效性主要是设计结构更接近操作系统惯用模型。ByteBuffer作为最核心的东西,内容也十分的多,本文也主要问绕这个类来写。
ByteBuffer继承自Buffer,Buffer中有四个比较关键的字段或者叫索引(mark, position, limit, capacity)。四个值有这样的关系:0 <= mark <= position <= limit <= capacity
private int mark = -1;
标记,设置一个标记位,调用mark()可以将mark的值设置为position。
private int position = 0;
位置,当前数据起始位置,调用put()添加数据,position会自增。随时保持最新数据的最后一个字节位置。调用position()可以得到当前position值,position(int)可以设置position的值。
private int limit;
界限,不能使用的数据位,即指向一段数据流末尾。调用get()方法返回的数据就是在position和limit之间的数据。调用limit()可以获取limit值,limit(int)设置limit值。
private int capacity;
容量,即该Buffer的大小,分配空间时候决定的,一直指向最后一个数据地址。
字节缓冲区的几个重要方法:
allocate(int):新建一个Buffer,分配指定size的空间。此时position = 0,limit = capacity,mark = null,所有元素将初始化为0。
allocateDirect(int):这个功能如上,比较牛的是与系统耦合性较高,因此速度更快,但是分配开支也会增大,数据位于常规垃圾回收管理之外。
get()/get(int):get()获取当前position的值,并且对position做自增,表示移动到下个位置。get(int)只取出指定位置的数据,不移动指针。
put(byte)/put(int, byte):put(byte)在position位置存入byte,对position做自增,移动到下个位置。put(int, byte)替换int指定位置的值为byte,不移动指针。
flip():在准备取缓冲区内所有数据的时候必须调用一次,进行这些操作:limit = position,position = 0,mark = -1,意味着之前标记将丢失,从0到limit进行遍历即可得到所有数据,写入也时同样的道理。
mark():对缓冲区的位置做标记,进行这个操作:mark = position。一般会配合reset()来使用,前者将当前位置记住,后者将当前位置设置为记住的位置,这有点像录音机中的A-B复读的意思。对于需要取一段特殊数据是有用的。
reset():重置缓冲区的position为先前mark的位置,进行这个操作:position = mark。如果mark < 0会抛InvalidMarkException异常,也就是没有调用mark()之前,不可以reset。 clear():重置缓冲区指针,进行这些操作:position = 0,limit = capacity,mark = -1。这个操作并不会删除实际的数据,但是指针位置被重置了,和flip()接近。
limit()/limit(int):分别是获取limit和设置limit。需要注意的是设置的时候不能超过capacity,不能小于0,如果position > limit,会将position也设置为limit,相当于缩小了范围。如果mark > limit,则mark = -1,作废。
position()/position(int):分别是获取和设置position,设置的时候不得大于limit,不得小于0,如果mark > position,则mark = -1,作废。
rewind():重绕缓冲区,进行这些操作:position = 0,mark = -1。可见也是类似flip(),可以为存取数据做准备,并且使mark作废。
remaining()/hasRemaining():前者得到剩余数,即position – limit的值。后者是判断position是否小于limit,小于返回true,可以用在取数据时判断是否还有数据。
等等,还有些就不一一介绍了。