Java NIO的Buffer本质上是一个内存块,既可以写入数据,也可以从中读取数据。Java NIO中代表缓冲区的Buffer类是一个抽象类,位于java.nio包中,与普通的内存块(Java数组)不同的是:NIO Buffer对象提供很多实用的方法,用来进行写入和读取的交替访问。

分类

Buffer类是一个抽象类,对应于Java的主要数据类型。在NIO中,有8种缓冲区类,ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer、MappedByteBuffer。前7种Buffer类型覆盖了能在IO中传输的所有Java基本数据类型,第8种类型是一种专门用于内存映射的ByteBuffer类型。

Buffer类中重要的属性

在Buffer的读写过程中,常常伴随着对几个属性的修改:position、limit、capacity、mark。读写模式下,这几个属性代表不同的含义。其中capacity是创建缓存区的时候指定的,已经定义则不会再修改。其他的三个属性不能大于capacity,否则会抛出缓冲区溢出的异常。其他三个属性的含义如下:

  • position:代表当前操作(读/写)的位置;
  • limit:代表当前操作(读/写)的限制位置,不能超过这个位置;
  • mark:用于临时记录当前的position,用于reset方法。

写缓存

写缓存是创建缓存区后的默认模式,此时limit和capacity是同一个位置,position指向0,mark默认-1。
当插入一个数据后,position加1后移,但是不能超过limit。

读缓存

当调用flip()方法后,缓存进入读模式,此时limit变为position,而position变为0,此时可以读的数据在0-limit的范围。

1
2
3
4
5
6
public final Buffer flip() {  
limit = position;
position = 0;
mark = -1;
return this;
}

缓存的读模式进入缓存的写模式,可以通过两种方法clear()compact(),前者直接将position置为0,limit置为capacity,写数据的时候,缓存中的数据将会被覆盖,第二种方法,则会将position和limit之间的数据重新拷贝到缓存的头部,不会丢数据。

remark的标记,将当前正操作的position临时保存,当调用reset()的时候,会把remark中的值恢复到position中,相当于可以重新读取数据。

Buffer类的常用方法

常用的方法:创建,写,读,重复读,标记,重置。

缓存的创建

直接调用不同缓存类型的allocate()方法,制定大小即可。

1
IntBuffer intBuffer = IntBuffer.allocate(10);

写缓存

调用put()方法,将数据放到缓存中,如:

1
2
3
4
for (int i = 0; i < 8; i++) {  
intBuffer.put(i+1);
}
printBuffer("put数据后", intBuffer);

结果为:

1
[put数据后]buffer的各个属性:	 position=8 limit=10 capacity=10  	数据:[1, 2, 3, 4, 5, 6, 7, 8, 0, 0]

读缓存

首先调用flip()切换到读模式,然后调用get()方法读取数据:

1
2
3
4
5
6
7
8
// 调用flip切换读缓存模式,flip之前get的数据可能是脏数据  
intBuffer.flip();
printBuffer("flip后", intBuffer);

// 读取数据
intBuffer.get();
intBuffer.get();
printBuffer("读2个后" , intBuffer);

结果如下:

1
2
[flip后]buffer的各个属性:	 position=0 limit=8 capacity=10  	数据:[1, 2, 3, 4, 5, 6, 7, 8, 0, 0]
[读2个后]buffer的各个属性: position=2 limit=8 capacity=10 数据:[1, 2, 3, 4, 5, 6, 7, 8, 0, 0]

重复读

调用rewind()方法,将position置为0,重新读取数据。
结果:

1
[rewind后]buffer的各个属性:	 position=0 limit=8 capacity=10  	数据:[1, 2, 3, 4, 5, 6, 7, 8, 0, 0]

读切换到写模式

clear方法:

1
2
intBuffer.clear();  
printBuffer("clear后", intBuffer);

结果:

1
[clear后]buffer的各个属性:	 position=0 limit=10 capacity=10  	数据:[1, 2, 3, 4, 5, 6, 7, 8, 0, 0]

compact方法:

1
2
intBuffer.compact();  
printBuffer("compact后", intBuffer);

结果:

1
[compact后]buffer的各个属性:	 position=6 limit=10 capacity=10  	数据:[3, 4, 5, 6, 7, 8, 7, 8, 0, 0]

两者的区别:compact拷贝剩余数据,而clear只变了位置。

标记重置

remark()reset()方法组合使用:

1
2
3
4
5
6
7
8
9
// remark记录位置 记录的position为6 
intBuffer.mark();

intBuffer.put(10).put(11);
printBuffer("reset前", intBuffer);
intBuffer.reset();
printBuffer("reset后", intBuffer);
intBuffer.put(100).put(110);
printBuffer("覆盖2个后", intBuffer);

结果:

1
2
3
[reset前]buffer的各个属性:	 position=8 limit=10 capacity=10  	数据:[3, 4, 5, 6, 7, 8, 10, 11, 0, 0]
[reset后]buffer的各个属性: position=6 limit=10 capacity=10 数据:[3, 4, 5, 6, 7, 8, 10, 11, 0, 0]
[覆盖2个后]buffer的各个属性: position=8 limit=10 capacity=10 数据:[3, 4, 5, 6, 7, 8, 100, 110, 0, 0]