Java NIO ByteBuffer 的 position,limit,capacity 和
状态变量
在缓冲区中,最重要的状态变量有下面三个,它们一起合作完成对缓冲区内部状态的变化跟踪:
position:指定了下一个将要被写入或者读取的元素索引,它的值由get()/put()方法自动更新,在新创建一个Buffer对象时,position被初始化为0。您可以回想一下,缓冲区实际上就是美化了的数组。在从通道读取时,您将所读取的数据放到底层的数组中。 position 变量跟踪已经写了多少数据。更准确地说,它指定了下一个字节将放到数组的哪一个元素中。因此,如果您从通道中读三个字节到缓冲区中,那么缓冲区的 position将会设置为3,指向数组中第四个元素。同样,在写入通道时,您是从缓冲区中获取数据。 position 值跟踪从缓冲区中获取了多少数据。更准确地说,它指定下一个字节来自数组的哪一个元素。因此如果从缓冲区写了5个字节到通道中,那么缓冲区的 position 将被设置为5,指向数组的第六个元素。position 总是小于或者等于 limit。
limit:指定还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。
capacity:指定了可以存储在缓冲区中的最大数据容量,实际上,它指定了底层数组的大小,或者至少是指定了准许我们使用的底层数组的容量。
以上三个属性值之间有一些相对大小的关系:0 <= position <= limit <= capacity。如果我们创建一个新的容量大小为10的ByteBuffer对象,在初始化的时候,position设置为0,limit和 capacity被设置为10,在以后使用ByteBuffer对象过程中,capacity的值不会再发生变化,而其它两个个将会随着使用而变化。三个属性值分别如图所示:
现在我们可以从通道中读取一些数据到缓冲区中,注意从通道读取数据,相当于往缓冲区中写入数据。如果读取4个自己的数据,则此时position的值为4,即下一个将要被写入的字节索引为4,而limit仍然是10
下一步把读取的数据写入到输出通道中,相当于从缓冲区中读取数据,在此之前,必须调用flip()方法,该方法将会完成两件事情:
1. 把limit设置为当前的position值
2. 把position设置为0
由于position被设置为0,所以可以保证在下一步输出时读取到的是缓冲区中的第一个字节,而limit被设置为当前的position,可以保证读取的数据正好是之前写入到缓冲区中的数据
在从缓冲区中读取数据完毕后,limit的值仍然保持在我们调用flip()方法时的值,调用clear()方法能够把所有的状态变化设置为初始化时的值,如下图所示:
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class Program {
public static void main(String args[]) throws Exception {
FileInputStream fin = new FileInputStream("d:\\test.txt");
FileChannel fc = fin.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(10);
output("初始化", buffer);
fc.read(buffer);
output("调用read()", buffer);
buffer.flip();
output("调用flip()", buffer);
while (buffer.remaining() > 0) {
byte b = buffer.get();
// System.out.print(((char)b));
}
output("调用get()", buffer);
buffer.clear();
output("调用clear()", buffer);
fin.close();
}
public static void output(String step, Buffer buffer) {
System.out.println(step + " : ");
System.out.print("capacity: " + buffer.capacity() + ", ");
System.out.print("position: " + buffer.position() + ", ");
System.out.println("limit: " + buffer.limit());
System.out.println();
}
}
访问方法
get() 方法,ByteBuffer 类中有四个 get() 方法:
1. byte get(); 第一个方法获取单个字节
2. ByteBuffer get( byte dst[] );第二和第三个方法将一组字节读到一个数组中
3. ByteBuffer get( byte dst[], int offset, int length );第二和第三个方法将一组字节读到一个数组中
4. byte get( int index );第四个方法从缓冲区中的特定位置获取字节
我们认为前三个 get() 方法是相对的,而最后一个方法是绝对的。 相对意味着 get() 操作服从 limit 和 position 值 ― 更明确地说,
字节是从当前 position 读取的,而 position 在 get 之后会增加。另一方面,一个 绝对方法会忽略 limit 和 position 值,也不会影响它们.
事实上,它完全绕过了缓冲区的统计方法。上面列出的方法对应于 ByteBuffer 类。其他类有等价的 get() 方法,这些方法除了不是处理字节外,
其它方面是是完全一样的,它们处理的是与该缓冲区类相适应的类型。
put()方法,ByteBuffer 类中有五个 put() 方法:
1. ByteBuffer put( byte b );第一个方法 写入(put) 单个字节
2. ByteBuffer put( byte src[] );第二和第三个方法写入来自一个数组的一组字节。
3. ByteBuffer put( byte src[], int offset, int length );
4. ByteBuffer put( ByteBuffer src );第四个方法将数据从一个给定的源ByteBuffer 写入这个ByteBuffer。
5. ByteBuffer put( int index, byte b );第五个方法将字节写入缓冲区中特定的 位置 。
与 get() 方法一样,我们将把 put() 方法划分为 相对 或者 绝对 的。前四个方法是相对的,
而第五个方法是绝对的。上面显示的方法对应于 ByteBuffer 类。其他类有等价的 put() 方法,
这些方法除了不是处理字节之外,其它方面是完全一样的。它们处理的是与该缓冲区类相适应的类型。
类型化的 get() 和 put() 方法:
除了前些小节中描述的 get() 和 put() 方法, ByteBuffer 还有用于读写不同类型的值的其他方法,如下所示,
getByte()、getChar()、getShort()、getInt()、getLong()、getFloat()、getDouble()、putByte()、putChar()、 putShort()、putInt()、putLong()、putFloat()、putDoubl