Buffer用于从channel中读(或者向channel写)数据,它有3个标记,分别是capacity, limit & position,其中capacity是此buffer的容量(固定值),buffer初始化时(使用allocate方法)设定;limit是有效数据的边界,即0~limit之间是可读/写的数据,position是下一个应该被读/写的位置。
Buffer有两种模式:写模式和读模式,写模式是向buffer中写数据(例如socketChannel.read(buffer)),读模式是从buffer中读出数据。
写模式下limit总等于capacity,读模式下limit是最近一次写入的数据位置。
使用buffer读写数据的基本过程是一个4步循环:
-
向buffer写入数据;
-
buffer.flip(); // 把buffer从写模式切换到读模式
-
从buffer中读出数据;
-
if (buffer.hasRemaining()) {buffer.compact();} else {buffer.clear();} // compact和clear方法将buffer切换回写模式,下面有解释
以上解释来自于Java NIO Buffer。
compact和clear方法
compact方法将未读完的数据(position与limit之间的数据)平移到buffer头部,然后将position设到这段数据的尾部,这样后面的写操作将在这段数据后追加(见SocketChannel.read()方法的javadoc),而不会覆盖这段数据,然后将limit设为capacity;clear方法令position=0; limit=capacity;从而为读数据做好准备,详见二者的javadoc。
wrap方法
ByteBuffer buffer = ByteBuffer.allocate(10); byte[] content = { 27, 91, 75, 116, 117 }; buffer = ByteBuffer.wrap(content); assertEquals(0, buffer.position()); assertEquals(5, buffer.limit()); assertEquals(5, buffer.capacity());
以上表明wrap方法会改变ByteBuffer对象的容量(capacity),并将其置为读模式;
实例演示
下面以ByteBuffer为例说明Java的buffer读写操作,以及对相关的几个参数的影响;
import java.nio.ByteBuffer; public class GetPutByteBuffer { public static void main(String[] args) { ByteBuffer byteBuffer = ByteBuffer.allocate(10); System.out.println("pos=" + byteBuffer.position() + " limit=" + byteBuffer.limit() + " capacity=" + byteBuffer.capacity()); // pos=0 limit=10 capacity=10 byteBuffer.put((byte) 20); System.out.println("pos=" + byteBuffer.position() + " limit=" + byteBuffer.limit()); // pos=1 limit=10 byteBuffer.put((byte) 33); System.out.println("pos=" + byteBuffer.position() + " limit=" + byteBuffer.limit()); // pos=2 limit=10 byteBuffer.put((byte) 55); byteBuffer.flip(); System.out.println("pos=" + byteBuffer.position() + " limit=" + byteBuffer.limit()); // pos=0 limit=3 System.out.println(byteBuffer.get()); // 20 System.out.println("pos=" + byteBuffer.position() + " limit=" + byteBuffer.limit()); // pos=1 limit=3 byteBuffer.rewind(); System.out.println(byteBuffer.get()); // 20 System.out.println("pos=" + byteBuffer.position() + " limit=" + byteBuffer.limit()); // pos=1 limit=3 System.out.println("the remaining is: " + byteBuffer.remaining()); // the remaining is: 2 while (byteBuffer.hasRemaining()) { System.out.println(byteBuffer.get()); System.out.println("pos=" + byteBuffer.position() + " limit=" + byteBuffer.limit()); } / * 33 * pos=2 limit=3 * 55 * pos=3 limit=3 / } }
可以看到buffer的capacity是一个固定值,初始化时设定,pos初始为0,limit初始等于capacity,一次读(get)或写(put)都使pos加一,limit不变,flip使limit=pos,pos=0,接收完数据开始读取时这个动作是必须的,rewind使pos=0,相当于重新开始读数据。