Buffer(缓冲器)
Java NIO中的Buffer用于和NIO中的Channel进行交互,交互时数据会从Channel读取到Buffer中,或从Buffer写入到Channel中,如图1所示。
图1 Buffer和Channel
从结构上来说,Buffer类似于一个数组,它可以保存多个类型相同的数据。从类型上来说,Buffer是一个抽象类,其子类有ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer,这些子类中最常用的是ByteBuffer和CharBuffer,其他则使用较少。
Buffer类的子类中并没有提供构造方法,因此不能通过构造方法来创建对象。要想创建Buffer对象,通常会通过子类中的static XxxBuffer allocate(int capacity)方法来实现,其中Xxx表示不同的数据类型,而capacity表示容量,此方法的含义是创建一个容量为capacity的XxxBuffer对象。例如创建一个容量为6的CharBuffer对象的语句如下:
CharBuffer buffer = CharBuffer.allocate(6);
在学习Buffer的使用之前,需要对Buffer中的三个重要概念有所理解,这三个概念分别是capacity(容量)、limit(界限)和position(位置),其含义如下:
● capacity(容量):缓冲区的容量表示该Buffer的最大数据容量,即最多可以存储多少数据。缓冲区的容量值不能为负数,也不能够改变。
● limit(界限):表示Buffer容器中不可被读取的区域的第一个索引,即位于Buffer容器中索引为0到limit之间的区域都可以进行读取操作。缓冲区的limit值从不为负,也从不大于其容量。
● position(位置):用于指定下一个可以被读写的缓冲区位置索引。新创建的Buffer对象,position的默认值为0,每进行一次读取或写入操作,position的值都会自动向后移动一步。如果向Buffer缓冲区中执行8次写入操作,那么position的值为8,即指向Buffer中的第9个元素的索引位置。
在Buffer类中,定义了很多方法,其常见方法如表1所示。
表1 Buffer类的常用方法
方法声明 | 功能描述 |
---|---|
int capacity() | 获取缓冲区的大小 |
Buffer clear() | 清除缓冲区,将position设置为0,limit设置为capacity |
Buffer flip() | 反转缓冲区,先将limit设置为当前position位置,然后再将position设置为0 |
boolean hasRemaining() | 判断当前位置(position)和界限(limit)之间是否还有元素 |
int limit | 获取Buffer的limit位置 |
Buffer limit(int newLimit) | 设置limit的值,并返回一个新的limit缓冲区对象 |
Buffer mark() | 设置Buffer的标记(mark),只能在0与position之间做标记 |
int position() | 获取Buffer中position的值 |
Buffer position(int newPosition) | 设置Buffer的position,并返回位置被修改之后的Buffer对象 |
int remaining() | 获取当前位置和界限之间的元素个数 |
Buffer reset() | 将此缓冲区的位置重置为先前标记的位置 |
Buffer rewind() | 倒带缓冲区,将position设置为0,并取消设置的标记 |
除上表中的方法外,Buffer的所有子类中都额外提供了put()和get()方法用于向Buffer中放入数据和取出数据。在使用put()和get()方法放入和取出数据时,Buffer既支持单个数据的访问,也支持批量数据的访问。
对Buffer类有了一定的了解后,下面通过一个具体的案例来演示Buffer的使用,如文件1所示。
文件1 Example18.java
1 import java.nio.CharBuffer;
2 public class Example18 {
3 public static void main(String[] args){
4 // 创建CharBuffer对象,并指定缓冲区容量大小为6
5 CharBuffer charBuffer = CharBuffer.allocate(6);
6 System.out.println("容量:" + charBuffer.capacity());
7 System.out.println("界限值:" + charBuffer.limit());
8 System.out.println("初始位置:" + charBuffer.position());
9 // 向CharBuffer对象中放入3个元素
10 charBuffer.put('x');
11 charBuffer.put('y');
12 charBuffer.put('z');
13 System.out.println("加入元素后的界限值:" + charBuffer.limit());
14 System.out.println("加入元素后的位置:" + charBuffer.position());
15 // 执行flip()方法
16 charBuffer.flip();
17 System.out.println("执行flip()后的界限值:" + charBuffer.limit());
18 System.out.println("执行flip()后的位置:" + charBuffer.position());
19 // 取出第1个元素
20 System.out.println("取出的第1个元素为:" + charBuffer.get());
21 System.out.println("取出后的界限值:" + charBuffer.limit());
22 System.out.println("取出后的位置:" + charBuffer.position());
23 // 执行clear()方法
24 charBuffer.clear();
25 System.out.println("执行clear()后的界限值:" + charBuffer.limit());
26 System.out.println("执行clear()后的位置:" + charBuffer.position());
27 // 取出第1个元素
28 System.out.println("取出的第1个元素为:" + charBuffer.get(0));
29 System.out.println("取出后的界限值:" + charBuffer.limit());
30 System.out.println("取出后的位置:" + charBuffer.position());
31 }
32 }
运行结果如图2所示。
图2 运行结果
文件1中,首先创建了一个容量为6的CharBuffer对象,并输出了该对象的容量、界限以及初识位置值;然后向CharBuffer对象中放入了3个字符元素,并输出了加入元素后的位置,从输出结果可以看出,元素位置已经发生了变化。接下来执行了flip()方法并再次输出界限值和位置,此时界限值已由原来的6变为3,位置由3变为了0,这是因为调用了flip()方法之后,limit会移动到到原来的position的位置,而position会被设置为0。接着通过get()方法取出了对象中的第1个元素,并输出此时的界限值和位置,从输出结果可以看出,界限值没有变,而位置变为1,这是因为取出一个元素后,position会向后移动一位。程序继续向下执行完clear()方法后,limit会被设置为与容量相等,而position会被设置为0。最后通过索引的方式取出第1个元素的值,并输出界限值和位置。从运行结果可以看出,执行完clear()方法之后,Buffer对象中的数据依然存在,并且通过索引取出元素值后,position的值并没有受到影响。