缓冲区Buffer抽象类型本身并没有提供读、写缓冲区的接口。真正的读写接口get( ) 和 put( ) 是由Buffer的子类提供,每一个 Buffer 子类的读、写接口都支持特定的数据类型,因此在顶层的 Buffer 类中并没有对读、写接口进行抽象地声明。
我们以Buffer的子类ByteBuffer 为例,这应该是我们在开发过程中使用得最为频繁的缓冲区子类,缓冲区类型的 get 和 put 函数同时提供了相对和绝对方式读取、写入缓冲区的接口。如下代码所示。
代码 ByteBuffer的读写方法
public abstract class ByteBuffer extends Buffer implements Comparable {
public abstract byte get( );
public abstract byte get (int index);
public abstract ByteBuffer put (byte b);
public abstract ByteBuffer put (int index, byte b);
}
相对读、写函数
不带索引参数的 get( ) 和 put( ) 读、写函数被调用时,缓冲区的position位置会在函数返回时向前推进一位。如下程序代码与逻辑视图描述了对字节缓冲区对象进行读、写操作时的状态变化。
分配字节缓冲器
代码 分配ByteBuffer缓冲区的代码
// 分配一个ByteBuffer缓冲区
ByteBuffer buffer = ByteBuffer.allocate(12);
图示 分配ByteBuffer缓冲区
写入字节缓冲器
代码 向ByteBuffer缓冲区连续写入6次数据
// 连续6次写入缓冲区
buffer.put((byte) 0)
.put((byte) 1)
.put((byte) 2)
.put((byte) 3)
.put((byte) 4)
.put((byte) 5);
图示 向ByteBuffer缓冲区连续写入6次数据后
代码 继续向ByteBuffer缓冲区连续读取入6次数据
// 连续6次读取缓冲区
buffer.get();
buffer.get();
buffer.get();
buffer.get();
buffer.get();
buffer.get();
图示 继续向ByteBuffer缓冲区连续读取6次数据后
由于缓冲区是一个有界的容器,由Limit属性作为读、写的边界约束,因此不能无限次的调用get( )和put( )函数。
当position已经指向缓冲区Limit所限定的界限时,仍然继续调用缓冲区的 put( )函数,会导致 put 方法抛出BufferOverflowException (上溢)异常。
写操作上溢异常
代码 ByteBuffer缓冲区put操作引发的上溢
buffer.put((byte) 0); // 当Position已经到达Limit时,继续调用一次put函数
图示 ByteBuffer缓冲区put操作引发的上溢
读操作下溢异常
当position已经指向缓冲区limit所限定的界限时,仍然继续调用缓冲区的get( )函数,会导致 get方法抛出BufferUnderflowException(下溢)异常。
代码 ByteBuffer缓冲区get操作引发的下溢
buffer.get() ; // 当Position已经到达Limit时,再次调用一次get函数
绝对读、写函数
带索引参数的 get(int index) 和 put(int index) 属于绝对位置的读、写调用函数,当前缓冲区的 position 位置值并不会因函数调用生改变。但是如果提供的 position 索引超出范围,比如为负数或大于等于 limit(因为 limit 才是缓冲区允许读、写的边界,而不是capacity,除非limit与capacity重合),也将抛出IndexOutOfBoundsException 异常,对index的检查如下代码所示。
代码 缓冲区绝对读、写函数的index检查
final int checkIndex(int i) {
if ((i < 0) || (i >= limit))
throw new IndexOutOfBoundsException();
return i;
}