缓冲区Buffer抽象类型本身并没有提供读、写缓冲区的接口。真正的读写接口get( ) 和 put( ) 是由Buffer的子类提供,每一个 Buffer 子类的读、写接口都支持特定的数据类型,因此在顶层的 Buffer 类中并没有对读、写接口进行抽象地声明。

我们以Buffer的子类ByteBuffer 为例,这应该是我们在开发过程中使用得最为频繁的缓冲区子类,缓冲区类型的 get 和 put 函数同时提供了相对和绝对方式读取、写入缓冲区的接口。如下代码所示。

代码 ByteBuffer的读写方法

  1. public abstract class ByteBuffer extends Buffer implements Comparable {
  2. public abstract byte get( );
  3. public abstract byte get (int index);
  4. public abstract ByteBuffer put (byte b);
  5. public abstract ByteBuffer put (int index, byte b);
  6. }

相对读、写函数

不带索引参数的 get( ) 和 put( ) 读、写函数被调用时,缓冲区的position位置会在函数返回时向前推进一位。如下程序代码与逻辑视图描述了对字节缓冲区对象进行读、写操作时的状态变化。

分配字节缓冲器

代码 分配ByteBuffer缓冲区的代码

// 分配一个ByteBuffer缓冲区
ByteBuffer buffer = ByteBuffer.allocate(12);

图示 分配ByteBuffer缓冲区
image.png

写入字节缓冲器

代码 向ByteBuffer缓冲区连续写入6次数据

// 连续6次写入缓冲区
buffer.put((byte) 0)
      .put((byte) 1)
      .put((byte) 2)
      .put((byte) 3)
      .put((byte) 4)
      .put((byte) 5);

图示 向ByteBuffer缓冲区连续写入6次数据后
image.png

代码 继续向ByteBuffer缓冲区连续读取入6次数据

// 连续6次读取缓冲区
buffer.get();
buffer.get();
buffer.get();
buffer.get();
buffer.get();
buffer.get();

图示 继续向ByteBuffer缓冲区连续读取6次数据后
image.png

由于缓冲区是一个有界的容器,由Limit属性作为读、写的边界约束,因此不能无限次的调用get( )和put( )函数。

当position已经指向缓冲区Limit所限定的界限时,仍然继续调用缓冲区的 put( )函数,会导致 put 方法抛出BufferOverflowException (上溢)异常。

写操作上溢异常

代码 ByteBuffer缓冲区put操作引发的上溢

buffer.put((byte) 0); // 当Position已经到达Limit时,继续调用一次put函数

图示 ByteBuffer缓冲区put操作引发的上溢
image.png

读操作下溢异常

当position已经指向缓冲区limit所限定的界限时,仍然继续调用缓冲区的get( )函数,会导致 get方法抛出BufferUnderflowException(下溢)异常。

代码 ByteBuffer缓冲区get操作引发的下溢

buffer.get() ; // 当Position已经到达Limit时,再次调用一次get函数

图示 ByteBuffer缓冲区get操作引发的下溢
image.png

绝对读、写函数

带索引参数的 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;
}