JDK8源码阅读笔记
切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

Buffer


一种用于存储特定原始类型数据的容器。

缓冲区是具有特定原始类型元素的线性、有限序列。除了其内容外,缓冲区的基本属性包括其容量(capacity)、限制(limit)和位置(position):

  • 缓冲区的容量是指它所能包含的元素数量。缓冲区的容量永远不会是负数,并且在设置之后永远不会改变。
  • 缓冲区的限制是指第一个不应该被读取或写的元素的索引。缓冲区的限制不会是负数,并且永远不会大于它的容量(capacity)。
  • 缓冲区的位置是指下一个要被读取或写的元素的索引。缓冲区的位置不会是负数,并且永远不会大于它的限制(limit)。

对于每种非布尔的原始类型此类都有一个对应的子类。

传输数据

这个类的每个子类定义了两类 get 和 put 操作:

  • 相对操作从当前位置开始读取或写入一个或多个元素,然后按传输的元素数量增加位置。如果请求超出了限制,那么相对 get 操作会抛出 BufferUnderflowException 异常,相对 put 操作会抛出 BufferOverflowException 异常;在任一情况下,都不会传输数据。
  • 绝对操作需要一个明确的元素索引,并且不影响位置。如果索引参数超出限制,绝对 get 和 put 操作会抛出IndexOutOfBoundsException异常。

数据当然也可以通过对应通道的输入/输出操作转移到缓冲区中或从缓冲区中转出,这些操作始终是相对于当前位置的。

标记和重置
缓冲区的标记(mark)是当调用reset方法时其位置将被重置到的索引。标记(mark)并不会总是被定义,但当它被定义时,它永远不会是负数,并且永远不会大于位置(position)。如果已定义标记,那么当位置(position)或限制(limit)被调整到一个比标记小的值时,标记将被丢弃。如果标记未定义,那么调用reset方法将导致抛出InvalidMarkException异常。

不变式
以下不变量适用于标记、位置、限制和容量值:
0 <= mark <= position <= limit <= capacity

新创建的缓冲区总是位置(position)为零,并且标记(mark)是未定义的。初始的限制(limit)可能是零,或者它可能是取决于缓冲区的类型以及构建方式的其他值。新分配的缓冲区的每个元素都被初始化为零。

清除、翻转、倒带
除了访问位置、限制和容量值以及标记和重置的方法外,此类还定义了对缓冲器的以下操作:

  • clear使缓冲区准备好进行新的一系列通道读取或相对 put 操作序列:它将限制(limit)设置为容量(capacity),将位置(position)设置为零。
  • flip使缓冲区准备好进行新的一系列通道写入或相对 get 操作序列:它将限制(limit)设置为当前位置(position),然后将位置(position)设置为零。
  • rewind使缓冲区准备好重新读取它已经包含的数据:它保持限制不变,并将位置设置为零。

只读缓冲区
每个缓冲区都是可读的,但不是每个缓冲区都是可写的。每个缓冲区类的变异方法被指定为可选操作,当在只读缓冲区上调用这些方法时,会抛出ReadOnlyBufferException异常。只读缓冲区不允许更改其内容,但其标记、位置和限制值是可变的。可以通过调用其isReadOnly方法来确定缓冲区是否为只读。

线程安全性
多个并发线程使用同一个缓冲区是不安全的。如果一个缓冲区要由多个线程使用,那么应该通过适当的同步来控制对该缓冲区的访问。

调用链
这个类中没有其他返回值要返回的方法被指定为返回它们被调用的缓冲区。这样可以将方法调用链接在一起;例如,语句:

b.flip(); 
b.position(23); 
b.limit(42);

可以用一个更紧凑的语句代替:

b. flip().position(23).limit(42);

1. static final int SPLITERATOR_CHARACTERISTICS

在缓冲区中维护的元素进行遍历和分割的拆分器的特性。

static final int SPLITERATOR_CHARACTERISTICS =
        Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

2. private int mark = -1

标记,不变式:mark <= position <= limit <= capacity


3. private int position = 0

位置。


4. private int limit

限制。


5. private int capacity

容量。


6. long address

仅由直接缓冲区使用。 注意:为了提高 JNI 中 GetDirectBufferAddress 的速度,这里提升了它。


7. Buffer(int mark, int pos, int lim, int cap)

在检查不变式之后,创建一个具有给定标记、位置、限制和容量的新缓冲区。

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;
    }
}

8. public final int capacity()

返回此缓冲区的容量。

public final int capacity() {
    return capacity;
}

9. public final int position()

返回此缓冲区的位置。

public final int position() {
    return position;
}

10. public final Buffer position(int newPosition)

设置此缓冲区的位置。如果标记已定义且大于新位置,则会将其丢弃。

public final Buffer position(int newPosition) {
    if ((newPosition > limit) || (newPosition < 0))
        throw new IllegalArgumentException();
    position = newPosition;
    if (mark > position) mark = -1;
    return this;
}

11. public final int limit()

返回此缓冲区的限制。

public final int limit() {
    return limit;
}

12. public final Buffer limit(int newLimit)

设置此缓冲区的限制。如果位置大于新限制,则将其设置为新限制。如果标记被定义并且大于新的限制,那么它将被丢弃。

public final Buffer limit(int newLimit) {
    if ((newLimit > capacity) || (newLimit < 0))
        throw new IllegalArgumentException();
    limit = newLimit;
    if (position > newLimit) position = newLimit;
    if (mark > newLimit) mark = -1;
    return this;
}

13. public final Buffer mark()

将此缓冲区的标记设置在其位置。

public final Buffer mark() {
    mark = position;
    return this;
}

14. public final Buffer reset()

将此缓冲区的位置重置为先前标记的位置。
调用此方法既不会更改也不会丢弃标记的值。

public final Buffer reset() {
    int m = mark;
    if (m < 0)
        throw new InvalidMarkException();
    position = m;
    return this;
}

15. public final Buffer clear()

清除此缓冲区。位置设置为零,限制设置为容量,标记被丢弃。
在使用一系列通道读取或放置操作来填充此缓冲区之前,请调用此方法。例如:

buf.clear();     // 准备用于读取的缓冲区 
in.read(buf);    // 读取数据

这种方法实际上并不会擦除缓冲区中的数据,但它被命名为擦除,因为它最常用于这种情况。

public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

16. public final Buffer flip()

翻转此缓冲区。将限制设置为当前位置,然后将位置设置为零。如果定义了标记,则会将其丢弃。
在一系列通道读取或放置操作之后,调用此方法为一系列通道写入或相对获取操作做准备。例如:

buf.put(magic);    // 准备标头
in.read(buf);      // 将数据读取到缓冲区的其余部分
buf.flip();        // 翻转缓冲区
out.write(buf);    // 将标头+数据写入通道

当数据从一个地方传输到另一个地方时,此方法经常与compact方法结合使用。

public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

17. public final Buffer rewind()

回退此缓冲区。位置被设置为零,标记被丢弃。
在一系列通道写入或获取操作之前调用此方法,假设已经适当设置了限制。例如:

out.write(buf);    // 写入剩余数据
buf.rewind();      // 回退缓冲区
buf.get(array);    // 复制数据进数组

18. public final int remaining()

返回当前位置和限制之间的元素数。

public final int remaining() {
    return limit - position;
}

19. public final boolean hasRemaining()

返回当前位置和限制之间是否有任何元素。

public final boolean hasRemaining() {
    return position < limit;
}

20. public abstract boolean isReadOnly()

返回当前缓冲区是否是只读的。


21. public abstract boolean hasArray()

判断这个缓冲区是否基于一个可访问的数组。
如果此方法返回true,则可以安全地调用arrayarrayOffset方法。


22. public abstract Object array()

返回支持此缓冲区的数组(可选操作)。
此方法旨在使基于数组的缓冲区能够更有效地传递给原生代码。具体的子类为这个方法提供了更强类型的返回值。
对此缓冲区内容的修改将导致返回数组的内容被修改,反之亦然。
在调用此方法之前先调用hasArray方法,以确保此缓冲区具有可访问的后备数组。


23. public abstract int arrayOffset()

返回缓冲区第一个元素的缓冲区后备数组中的偏移量(可选操作)。
如果此缓冲区是基于数组的,则缓冲区位置p对应于数组索引p + arrayOffset()。
在调用此方法之前先调用hasArray方法,以确保此缓冲区具有可访问的后备数组。


24. public abstract boolean isDirect()

返回这个缓冲区是否是直接缓冲区。
当且仅返回true时此缓冲区是直接缓冲区。


25. final int nextGetIndex()

根据限制检查当前位置,如果当前位置不小于限制,则抛出BufferUnderflowException,否则递增位置。
返回其递增之前的当前位置值。

final int nextGetIndex() {                          // package-private
    int p = position;
    if (p >= limit)
        throw new BufferUnderflowException();
    position = p + 1;
    return p;
}

26. final int nextGetIndex(int nb)

和上面的nextGetIndex方法类似,只不过上面的方法是尝试给位置+1,而此方法尝试给当前位置加指定的数量(参数nb)。

final int nextGetIndex(int nb) {                    // package-private
    int p = position;
    if (limit - p < nb)
        throw new BufferUnderflowException();
    position = p + nb;
    return p;
}

27. final int nextPutIndex()

根据限制检查当前位置,如果不小于限制,则抛出BufferOverflowException,否则递增位置。
返回其递增之前的当前位置值。

final int nextPutIndex() {                          // package-private
    int p = position;
    if (p >= limit)
        throw new BufferOverflowException();
    position = p + 1;
    return p;
}

28. final int nextPutIndex(int nb)

和上面的nextPutIndex方法类似,只不过上面的方法是尝试给位置+1,而此方法尝试给当前位置加指定的数量(参数nb)。

final int nextPutIndex(int nb) {                    // package-private
    int p = position;
    if (limit - p < nb)
        throw new BufferOverflowException();
    position = p + nb;
    return p;
}

29. final int checkIndex(int i)

根据限制检查给定的索引,如果该索引不小于限制或小于零,则抛出IndexOutOfBoundsException

final int checkIndex(int i) {                       // package-private
    if ((i < 0) || (i >= limit))
        throw new IndexOutOfBoundsException();
    return i;
}

30. final int checkIndex(int i, int nb)

检查索引是否还能增加nb

final int checkIndex(int i, int nb) {               // package-private
    if ((i < 0) || (nb > limit - i))
        throw new IndexOutOfBoundsException();
    return i;
}

31. final int markValue()

返回标记。

final int markValue() {                             // package-private
    return mark;
}

32. final void truncate()

截断。

final void truncate() {                             // package-private
    mark = -1;
    position = 0;
    limit = 0;
    capacity = 0;
}

33. final void discardMark()

放弃标记。

final void discardMark() {                          // package-private
    mark = -1;
}

34. static void checkBounds(int off, int len, int size)

检查边界。

static void checkBounds(int off, int len, int size) { // package-private
    if ((off | len | (off + len) | (size - (off + len))) < 0)
        throw new IndexOutOfBoundsException();
}