5.4.3 存储和接收数据

只要有了缓冲区,就可以用它来存放数据了。作为数据的“容器”,缓冲区既可用来输入也可用来输出。这一点就与流不同,流只能向一个方向传递数据。使用put()方法可以将数据放入缓冲区,使用get()方法则可以从缓冲区获取数据。信道的read()方法隐式调用了给定缓冲区的put(),而其write()方法则隐式调用了缓冲区的get()方法。下面展示了ByteBuffer的get()和put()方法,当然,其他类型的缓冲区也有类似的方法。

ByteBuffer:获取和存放字节

相对位置:

figure_0137_0171

绝对位置:

figure_0137_0172

有两种类型的get()和put():基于相对位置和基于绝对位置。基于相对位置的版本根据position的当前值,从“下一个”位置读取或存放数据,然后根据数据量给position增加适当的值(即,单字节形式增加1,数组形式增加array.length,数组/偏移量/长度形式则增加length)。也就是说,每次调用put()方法,都是在缓冲区中的已有元素后面追加数据,每次调用get()方法,都是读取缓冲区的后续元素。不过,如果这些操作会导致position的值超出limit的限制,get()方法将抛出BufferUnderflowException异常,put()方法将抛出BufferOverflowException异常。例如,如果传给get()方法的目标数组长度大于缓冲区的剩余空间大小,get()方法将抛出BufferUnderflowException异常,部分数据的get/put是不允许的。基于绝对位置的get()和put()以指定的索引位置为参数,从该位置读取数据或向该位置写入数据。绝对位置形式的get和put不会改变position的值。如果给定的索引值超出了limit的限制,它们将抛出IndexOutOfBoundsException异常。

除了字节类型外,ByteBuffer类还提供了其他类型数据的相当位置和绝对位置的get/put方法。这样一来,就有点像DataOutputStream了。

ByteBuffer:读取和存放Java多字节基本数据

figure_0138_0173

figure_0138_0174

其中“<Type>”代表Char、Double、Int、Long、Short之一,而“<type>”代表char、double、int、long、short之一。

每次调用基于相对位置的put()或get()方法,都将根据特定参数类型的长度增加position的值:short加2,int加4等。不过,如果这样做会导致position的值超出limit的限制,get()和put()方法将分别抛出BufferUnderflowException和BufferOverflowException异常:get和put不允许只对部分数据进行操作。发生了下溢/上溢(under/overflow)时,position的值不变。

可能你已经注意到,很多get/put方法都返回一个ByteBuffer。实际上它们返回的就是调用它们的那个ByteBuffer。这样做可以实现链式调用(call chaining),即第一次调用的结果可以直接用来进行后续的方法调用。例如,可以像下面那样将整数1和2存入ByteBuffer实例myBuffer中:

figure_0139_0175

回顾第3章的内容我们知道,多字节数据类型有一个字节顺序,称为big-endian或little-endian。Java默认使用big-endian。通过使用内置的ByteOrder.BIG_ENDIAN和ByteOrder.LITTLE_ENDIAN实例,可以获取和设定多字节数据类型写入字节缓冲区时的字节顺序。

ByteBuffer:缓冲区中的字节顺序

figure_0139_0176

第一个方法以ByteOrder常量的形式返回缓冲区的当前字节顺序。第二个方法用来设置写多字节数据时的字节顺序。

下面来看一个使用字节顺序的例子:

figure_0139_0177

看了这些有关字节顺序的讨论,你可能希望知道自己的处理器是什么字节顺序,ByteOrder定义了一个方法来解答这个问题:

ByteOrder:查找字节顺序

figure_0139_0178

nativeOrder()方法返回常量BIG_ENDIAN或LITTLE_ENDIAN之一。