Buffer缓冲区

Buffer是一个对象,它包含一些要写入或者要读出的数据。在NIO类库中加入Buffer对象,体现了新库与原I/O的一个重要区别。在面向流的I/O中,可以将数据直接写入或者将数据直接读到Stream对象中。

Buffer缓冲区类型

在NIO库中,所有数据都是用缓冲区处理的。在读写数据时,它是直接读到缓冲区中的。
缓冲区实质就是一个数组,通常它是一个字节数组(ByteBuffer),但是也有其他种类数组,类型及继承关系如图1所示。缓冲区还提供了对数据的结构化访问以及维护读写位置(limit)等信息。



图1 缓冲区类型及继承关系

事实上,对于每个非 boolean 基本类型,此类都有一个子类与之对应,每一个Buffer类都是Buffer接口的一个子实例。除了ByteBuffer,每个Buffer类都有完全一样的操作。大多数标准I/O操作都使用ByteBuffer,所以它在具有一般缓冲区的操作之外还提供了一些特有的操作,以方便网络读写。

Buffer的功能

一个用于特定基本类型数据的容器。

  • 缓冲区是特定基本类型元素的线性有限序列。除内容外,缓冲区的基本属性还包括容量、限制和位置:

  • 缓冲区的容量capacity 是它所包含的元素的数量。缓冲区的容量不能为负并且不能更改。

  • 缓冲区的限制limit 是第一个不应该读取或写入的元素的索引。缓冲区的限制不能为负,并且不能大于其容量。

  • 缓冲区的位置position 是下一个要读取或写入的元素的索引。缓冲区的位置不能为负,并且不能大于其限制。

传输数据

此类的每个子类都定义了两种获取和放置操作:

相对 操作读取或写入一个或多个元素,它从当前位置开始,然后将位置增加所传输的元素数。如果请求的传输超出限制,则相对获取 操作将抛出 BufferUnderflowException,相对放置 操作将抛出 BufferOverflowException;这两种情况下,都没有数据被传输。

绝对 操作采用显式元素索引,该操作不影响位置。如果索引参数超出限制,绝对获取 操作和放置 操作将抛出 IndexOutOfBoundsException。

当然,通过适当通道的 I/O 操作(通常与当前位置有关)也可以将数据传输到缓冲区或从缓冲区传出数据。

做标记和重置

缓冲区的标记 是一个索引,在调用 reset 方法时会将缓冲区的位置重置为该索引。并非总是需要定义标记,但在定义标记时,不能将其定义为负数,并且不能让它大于位置。如果定义了标记,则在将位置或限制调整为小于该标记的值时,该标记将被丢弃。如果未定义标记,那么调用 reset 方法将导致抛出 InvalidMarkException。

不变式

标记、位置、限制和容量值遵守以下不变式:

0 <= 标记 <= 位置 <= 限制 <= 容量
新创建的缓冲区总有一个 0 位置和一个未定义的标记。初始限制可以为 0,也可以为其他值,这取决于缓冲区类型及其构建方式。一般情况下,缓冲区的初始内容是未定义的。

清除、反转和重绕

除了访问位置、限制、容量值的方法以及做标记和重置的方法外,此类还定义了以下可对缓冲区进行的操作:

clear() 使缓冲区为一系列新的通道读取或相对放置 操作做好准备:它将限制设置为容量大小,将位置设置为 0。

flip() 使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为 0。

rewind() 使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。

只读缓冲区

每个缓冲区都是可读取的,但并非每个缓冲区都是可写入的。每个缓冲区类的转变方法都被指定为可选操作,当对只读缓冲区调用时,将抛出 ReadOnlyBufferException。只读缓冲区不允许更改其内容,但其标记、位置和限制值是可变的。可以调用其 isReadOnly 方法确定缓冲区是否为只读。

线程安全

多个当前线程使用缓冲区是不安全的。如果一个缓冲区由不止一个线程使用,则应该通过适当的同步来控制对该缓冲区的访问。

调用链

指定此类中的方法返回调用它们的缓冲区(否则它们不会返回任何值)。此操作允许将方法调用组成一个链;例如,语句序列

b.flip();
b.position(23);
b.limit(42);
可以由以下更紧凑的一个语句代替
b.flip().position(23).limit(42);

方法摘要

类型 方法 描述
abstract Object array() 返回此缓冲区的底层实现数组(可选操作)。
abstract int arrayOffset() 返回此缓冲区的底层实现数组中第一个缓冲区元素的偏移量(可选操作)。
int capacity() 返回此缓冲区的容量。
Buffer clear() 清除此缓冲区。
Buffer flip() 反转此缓冲区。
abstract boolean hasArray() 告知此缓冲区是否具有可访问的底层实现数组。
boolean hasRemaining() 告知在当前位置和限制之间是否有元素。
abstract boolean isDirect() 告知此缓冲区是否为直接缓冲区。
abstract boolean isReadOnly() 告知此缓冲区是否为只读缓冲区。
int limit() 返回此缓冲区的限制。
Buffer limit(int newLimit) 设置此缓冲区的限制。
Buffer mark() 在此缓冲区的位置设置标记。
int position() 返回此缓冲区的位置。
Buffer position(int newPosition) 设置此缓冲区的位置。
int remaining() 返回当前位置与限制之间的元素数。
Buffer reset() 将此缓冲区的位置重置为以前标记的位置。
Buffer rewind() 重绕此缓冲区。

缓冲区操作细节

开辟缓冲区



图2 开辟缓冲区

向缓冲区中增加一个数据



图3 向缓冲区中增加一个数据

向缓冲区中增加一组数据



图4 向缓冲区中增加一组数据

执行flip()方法,limit设置为position,position设置为0



图5 执行flip()方法

Adhere to the original technology to share, your support will encourage me to continue to create!