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()方法