jdk1.8解释:
Class Buffer
java.lang.Object
java.nio.Buffer
已知直接子类:
ByteBuffer , CharBuffer , DoubleBuffer , FloatBuffer , IntBuffer , LongBuffer , ShortBuffer
public abstract class Buffer
extends Object
用于特定原始类型的数据的容器。
缓冲器是特定原始类型的元素的线性有限序列。 除了其内容,缓冲区的基本属性是其容量,限制和位置:
A buffer’s capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes.
A buffer’s limit is the index of the first element that should not be read or written. A buffer’s limit is never negative and is never greater than its capacity.
A buffer’s position is the index of the next element to be read or written. A buffer’s position is never negative and is never greater than its limit.
每个非布尔基元类型都有这个类的一个子类。
传输数据
该类的每个子类定义了两类get和put操作:
Relative operations read or write one or more elements starting at the current position and then increment the position by the number of elements transferred. If the requested transfer exceeds the limit then a relative get operation throws a BufferUnderflowException and a relative put operation throws a BufferOverflowException; in either case, no data is transferred.
Absolute operations take an explicit element index and do not affect the position. Absolute get and put operations throw an IndexOutOfBoundsException if the index argument exceeds the limit.
当然,数据也可以通过相对于当前位置的相应通道的I / O操作被传送到或者从缓冲器传出。
标记和重置
当调用reset方法时,缓冲区的标记是其位置将被重置的索引。 标记并不总是定义,但是当它被定义时,它不会是负的,并且永远不会大于位置。 如果标记被定义,则当位置或极限被调整为小于标记的值时,它被丢弃。 如果未定义标记,则调用reset方法将导致抛出InvalidMarkException 。
不变量
标记,位置,极限和容量值的以下不变量保持不变:
0 <= mark <= position <= limit <= capacity
新创建的缓冲区始终具有零位置和未定义的标记。 初始限制可以为零,或者可以是取决于缓冲器的类型和构造方式的某些其他值。 新分配的缓冲区的每个元素被初始化为零。
清理,翻转和倒带
除了访问位置,限制和容量值以及标记和重置的方法之外,此类还定义了缓冲区上的以下操作:
clear()使缓冲区准备好信道读取或相对放置操作的一个新的序列:它设置了限制的能力和位置为零。
flip()使缓冲区准备好新的通道写入或相对获取操作序列:它将限制设置为当前位置,然后将位置设置为零。
rewind()使缓冲区准备好重新读取已经包含的数据:它保持限制不变,并将位置设置为零。
只读缓冲区
每个缓冲区都是可读的,但并不是每个缓冲区都是可写的。 每个缓冲区类的变异方法被指定为可选操作 ,当在只读缓冲区上调用时,它将抛出一个ReadOnlyBufferException 。 只读缓冲区不允许更改其内容,但其标记,位置和限制值是可变的。 缓冲区是否为只读可以通过调用其isReadOnly方法来确定 。
线程安全
缓冲区不能安全地被多个并发线程使用。 如果一个缓冲区被多个线程使用,则应该通过适当的同步来控制对缓冲区的访问。
调用链接
指定此类中没有值返回值的方法返回调用它们的缓冲区。 这允许方法调用被链接; 例如,语句序列
b.flip();
b.position(23);
b.limit(42);
可以由单一,更紧凑的语句替代
b.flip().position(23).limit(42);
Buffer 类是 java.nio 的构造基础。一个 Buffer 对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里,数据可被存储并在之后用于检索。缓冲区可以被写满或释放。对于每个非布尔原始数据类型都有一个缓冲区类,即 Buffer 的子类有:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer,是没有 BooleanBuffer 之说的。尽管缓冲区作用于它们存储的原始数据类型,但缓冲区十分倾向于处理字节。非字节缓冲区可以在后台执行从字节或到字节的转换,这取决于缓冲区是如何创建的。
◇ 缓冲区的四个属性
所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息,这四个属性尽管简单,但其至关重要,需熟记于心:
容量(Capacity):缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
上界(Limit):缓冲区的第一个不能被读或写的元素。缓冲创建时,limit 的值等于 capacity 的值。假设 capacity = 1024,我们在程序中设置了 limit = 512,说明,Buffer 的容量为 1024,但是从 512 之后既不能读也不能写,因此可以理解成,Buffer 的实际可用大小为 512。
位置(Position):下一个要被读或写的元素的索引。位置会自动由相应的 get() 和 put() 函数更新。 这里需要注意的是positon的位置是从0开始的。
标记(Mark):一个备忘位置。标记在设定前是未定义的(undefined)。使用场景是,假设缓冲区中有 10 个元素,position 目前的位置为 2(也就是如果get的话是第三个元素),现在只想发送 6 - 10 之间的缓冲数据,此时我们可以 buffer.mark(buffer.position()),即把当前的 position 记入 mark 中,然后 buffer.postion(6),此时发送给 channel 的数据就是 6 - 10 的数据。发送完后,我们可以调用 buffer.reset() 使得 position = mark,因此这里的 mark 只是用于临时记录一下位置用的。
请切记,在使用 Buffer 时,我们实际操作的就是这四个属性的值。我们发现,Buffer 类并没有包括 get() 或 put() 函数。但是,每一个Buffer 的子类都有这两个函数,但它们所采用的参数类型,以及它们返回的数据类型,对每个子类来说都是唯一的,所以它们不能在顶层 Buffer 类中被抽象地声明。它们的定义必须被特定类型的子类所遵从。若不加特殊说明,我们在下面讨论的一些内容,都是以 ByteBuffer 为例,当然,它当然有 get() 和 put() 方法了。
Buffer(int mark, int pos, int lim, int cap) { // package-private
if (cap < 0)
throw new IllegalArgumentException("Negative capacity: " + cap);
this.capacity = cap;
limit(lim);
position(pos);
if (mark >= 0) {
if (mark > pos)
throw new IllegalArgumentException("mark > position: ("
+ mark + " > " + pos + ")");
this.mark = mark;
}
}
public final Buffer limit(int newLimit) {
if ((newLimit > capacity) || (newLimit < 0))
throw new IllegalArgumentException();
limit = newLimit;
if (position > limit) position = limit;
if (mark > limit) mark = -1;
return this;
}
public final Buffer position(int newPosition) {
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
if (mark > position) mark = -1;
return this;
}
从上面的源代码可以看出,构造方法是对4个属性设置值,中间加了public final Buffer limit(int newLimit);和public final Buffer position(int newPosition);方法的限制,主要是规范4个属性的范围
(1)
public final int capacity() {
return capacity;
}
public final int position() {
return position;
}
public final int limit() {
return limit;
}
上面的三个方法是直接返回的属性值;
(2)
public final Buffer mark() {
mark = position;
return this;
}
这个方法可以看出,把当属性position的位置赋值给属性mark,可以理解利用mark记住当前标记的位置
jdk1.8解释:
public final Buffer mark()
将此缓冲区的标记设置在其位置。
结果
这个缓冲区
(3)
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
该方法把属性mark的值赋值给属性position,可以理解把标记返回到上次标记位置
jdk1.8解释:
public final Buffer reset()
将此缓冲区的位置重置为先前标记的位置。
调用此方法既不会更改也不丢弃该标记的值。
结果
这个缓冲区
异常
InvalidMarkException - 如果标记尚未设置
(4)
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
该方法可以看出,重置position ,limit ,mark 三个属性值
jdk1.8解释:
public final Buffer clear()
清除此缓冲区。 位置设置为零,限制设置为容量,标记被丢弃。
在使用一系列通道读取或放置操作填充此缓冲区之前调用此方法。 例如:
buf.clear(); // Prepare buffer for reading
in.read(buf); // Read data
这个方法实际上并不会清除缓冲区中的数据,但是它被命名为它的确是因为它最常用于情况也是如此。
结果
这个缓冲区
(5)
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
该方法跟clean方法差不多,只是把属性上界limit设置成position,
看jdk1.8解释:
public final Buffer flip()
翻转这个缓冲区。 该限制设置为当前位置,然后将该位置设置为零。 如果标记被定义,则它被丢弃。
在通道读取或放置操作的序列之后,调用此方法来准备一系列通道写入或相对获取操作。 例如:
buf.put(magic); // Prepend header
in.read(buf); // Read data into rest of buffer
buf.flip(); // Flip buffer
out.write(buf); // Write header + data to channel
当将数据从一个地方传输到另一个地址时,该方法通常与compact方法结合使用。
结果
这个缓冲区
(6)
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
设置属性position 和mark
看jdk1.8解释:
public final Buffer rewind()
倒带这个缓冲区。 位置设置为零,标记被丢弃。
在通道写入或获取操作的序列之前调用此方法,假设已经设置了相应的限制。 例如:
out.write(buf); // Write remaining data
buf.rewind(); // Rewind buffer
buf.get(array); // Copy data into array
结果
这个缓冲区
(7)
public final int remaining() {
return limit - position;
}
public final boolean hasRemaining() {
return position < limit;
}
这两个方法,一个返回上界limit - 位置position,另一个判断 position < limit
jdk1,8 解释:
public final int remaining()
返回当前位置和限制之间的元素数。
结果
此缓冲区中剩余的元素数
___________________________________
public final boolean hasRemaining()
告诉当前位置和极限之间是否存在任何元素。
结果
true如果,并且只有在此缓冲区中至少有一个元素
(8) 几个抽象方法,需要子类实现
public abstract boolean isReadOnly()
告知这个缓冲区是否是只读的。
结果
true如果,只有这个缓冲区是只读的
public abstract boolean hasArray()
告诉这个缓冲区是否由可访问的数组支持。
如果此方法返回true,则可以安全地调用array和arrayOffset方法。
结果
true如果并且只有这个缓冲区由数组支持并且不是只读的
public abstract Object array()
返回支持此缓冲区的数组(可选操作) 。
该方法旨在使阵列支持的缓冲区更有效地传递到本地代码。 具体的子类为此方法提供了更强类型的返回值。
对此缓冲区内容的修改将导致返回的数组的内容被修改,反之亦然。
在调用此方法之前调用hasArray方法,以确保此缓冲区具有可访问的后台阵列。
结果
支持这个缓冲区的数组
异常
ReadOnlyBufferException - 如果此缓冲区由数组支持但是只读
UnsupportedOperationException - 如果此缓冲区未由可访问阵列支持
public abstract int arrayOffset()
返回该缓冲区的缓冲区的第一个元素的背衬数组中的偏移量(可选操作) 。
如果此缓冲区由数组支持,那么缓冲位置p对应于数组索引p + arrayOffset() 。
在调用此方法之前调用hasArray方法,以确保此缓冲区具有可访问的后备数组。
结果
缓冲区的第一个元素的缓冲区数组中的偏移量
异常
ReadOnlyBufferException - 如果此缓冲区由数组支持但是只读
UnsupportedOperationException - 如果此缓冲区不由可访问阵列支持
public abstract boolean isDirect()
告诉这个缓冲区是否是 direct 。
结果
true如果,只有这个缓冲区是直接的
未完待结