基本概念
一个用于特定基本类型数据的容器。缓冲区是特定基本类型元素的线性有限序列。除内容外,缓冲区的基本属性还包括容量、限制和位置:
- 缓冲区的容量 是它所包含的元素的数量。缓冲区的容量不能为负并且不能更改。
- 缓冲区的限制 是第一个不应该读取或写入的元素的索引。缓冲区的限制不能为负,并且不能大于其容量。
- 缓冲区的位置 是下一个要读取或写入的元素的索引。缓冲区的位置不能为负,并且不能大于其限制。
1:标记和重置
缓冲区的标记 是一个索引,在调用 reset 方法时会将缓冲区的位置重置为该索引。并非总是需要定义标记,但在定义标记时,不能将其定义为负数,并且不能让它大于位置。如果定义了标记,则在将位置或限制调整为小于该标记的值时,该标记将被丢弃。如果未定义标记,那么调用 reset 方法将导致抛出 InvalidMarkException。
2:不变式
0 <= 标记 <= 位置 <= 限制 <= 容量
新创建的缓冲区总有一个 0 位置和一个未定义的标记。初始限制可以为 0,也可以为其他值,这取决于缓冲区类型及其构建方式。一般情况下,缓冲区的初始内容是未定义的。
3:清除、反转和重绕
除了访问位置、限制、容量值的方法以及做标记和重置的方法外,此类还定义了以下可对缓冲区进行的操作:
- clear() 使缓冲区为一系列新的通道读取或相对放置操作做好准备:它将限制设置为容量大小,将位置设置为 0。
- flip() 使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为 0。
- rewind() 使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。
4:只读缓冲区
每个缓冲区都是可读取的,但并非每个缓冲区都是可写入的。每个缓冲区类的转变方法都被指定为可选操作,当对只读缓冲区调用时,将抛出 ReadOnlyBufferException。只读缓冲区不允许更改其内容,但其标记、位置和限制值是可变的。可以调用其 isReadOnly 方法确定缓冲区是否为只读。
5:线程安全
多个当前线程使用缓冲区是不安全的。如果一个缓冲区由不止一个线程使用,则应该通过适当的同步来控制对该缓冲区的访问。
6:调用链
指定此类中的方法返回调用它们的缓冲区(否则它们不会返回任何值)。此操作允许将方法调用组成一个链;例如,语句序列
b.flip();
b.position(23);
b.limit(42);
可以由以下更紧凑的一个语句代替
b.flip().position(23).limit(42);
字节缓冲区-ByteBuffer
public abstract class ByteBuffer
extends Buffer implements Comparable<ByteBuffer>
此类针对字节缓冲区定义了以下六类操作:
- 读写单个字节的绝对和相对 get 和 put 方法
- 将此缓冲区的连续字节序列传输到数组中的相对批量 get 方法
- 将 byte 数组或其他字节缓冲区中的连续字节序列传输到此缓冲区的相对批量 put 方法
- 读写其他基本类型值,并按照特定的字节顺序在字节序列之间转换这些值的绝对和相对 get 和 put 方法
- 创建视图缓冲区 的方法,这些方法允许将字节缓冲区视为包含其他基本类型值的缓冲区
- 对字节缓冲区进行 compacting、duplicating 和 slicing 的方法
1:直接与 非直接缓冲区
- 字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)
- 直接字节缓冲区可以通过调用此类的 allocateDirect 工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们
- 直接字节缓冲区还可以通过 mapping 将文件区域直接映射到内存中来创建。Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。
- 字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect 方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。
2: 访问二进制数据
- 此类定义了除 boolean 之外,读写所有其他基本类型值的方法。这些基本值可以根据缓冲区的当前字节顺序与字节序列互相进行转换,并可以通过 order 方法获取和修改。特定的字节顺序由 ByteOrder 类的实例表示。字节缓冲区的初始顺序始终是 BIG_ENDIAN。
- 为了访问异类二进制数据(即其他类型的值的序列),此类还针对每种类型定义了一系列绝对和相对的 get 和 put 方法。例如,对于 32 位浮点值,此类定义了以下方法:
float getFloat()
float getFloat(int index)
void putFloat(float f)
void putFloat(int index, float f)
并针对 char、short、int、long 和 double 等类型定义了相应的方法。绝对 get 和 put 方法的 index 参数是根据字节定义的,而不是根据所读写的类型定义的。
与上述特定于类型的 get 和 put 方法系列相比,视图缓冲区有以下三大主要优势:
- 视图缓冲区不是根据字节进行索引,而是根据其特定于类型的值的大小进行索引
- 视图缓冲区提供了相对批量 get 和 put 方法,这些方法可在缓冲区和数组或相同类型的其他缓冲区之间传输值的连续序列
- 视图缓冲区可能更高效,这是因为,当且仅当其支持的字节缓冲区为直接缓冲区时它才是直接缓冲区
其它Buffer
- 字符缓冲区
public abstract class CharBuffer
extends Buffer
implements Comparable<CharBuffer>, Appendable, CharSequence, Readable
- Double 缓冲区
public abstract class DoubleBuffer
extends Buffer
implements Comparable<DoubleBuffer>
- Float 缓冲区
public abstract class FloatBuffer
extends Buffer
implements Comparable<FloatBuffer>
- int 缓冲区
extends Buffer
implements Comparable<IntBuffer>
- long 缓冲区
public abstract class LongBuffer
extends Buffer
implements Comparable<LongBuffer>
- short 缓冲区
ublic abstract class ShortBuffer
extends Buffer
implements Comparable<ShortBuffer>