介绍
首先 Buffer 是一个能存储基本数据类型的容器(除了 Boolean 类型),从 java.nio 包的继承结构就能看出来。
Java中的Buffer类是一个抽象类。Buffer类提供了一种将数据存储在内存中的方式,并提供了一些操作数据的方法。Buffer类可以存储不同类型的数据,并且可以方便地进行读取和写入操作。
常用的Buffer类有ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer和DoubleBuffer。这些Buffer类分别用于存储不同类型的数据,并且提供了相应的读写操作方法。
常量&变量
/**
* The characteristics of Spliterators that traverse and split elements
* maintained in Buffers.
* 在缓冲区中维护遍历和分割元素的拆分特征 TODO
*/
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
// Invariants: mark <= position <= limit <= capacity
/**
* 一个新创建的 Buffer 具有以下几个性质:
*
* 它的 position 是 0;
* mark 没有被定义(实际上是 -1);
* 而 limit 值可能是 0,也可能是其他值,这取决于这个 Buffer 的类型;
* Buffer 中每一个元素值都被初始化为 0
*/
//当调用 reset() 方法被调用时,一个 Buffer 的 mark 值会被设定为当前的 position 值的大小。
//如果mark=-1 ,会reset失败
private int mark = -1;
//它是下一个要读或者写的元素的索引,它是非负的且不会超过 limit 的大小。
private int position = 0;
//它是可以读或者写的最后一个元素的索引,它是非负的且不会超过 capacity 的大小。
private int limit;
//它表示一个 Buffer 包含的元素数量,它是非负且恒定不变的
private int capacity;
// Used only by direct buffers
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
//这个属性只有在当前 Buffer 为 Direct Buffer 时才会使用
long address;
- Capacity(容量):表示Buffer对象中可以存储的数据的最大数量。该值不能被更改。
- Position(位置):表示当前向Buffer对象中读取或写入数据的位置。
- Limit(限制):表示Buffer对象中可以读取或写入的数据的范围,即limit之后的数据不能读取或写入。
- Mark(标记):表示当前位置的一个备份,在调用reset()方法时,将回到该备份的位置。
构造方法
创建一个包含给定标记、位置、限制和容量的缓冲区。
// Creates a new buffer with the given mark, position, limit, and capacity,
// after checking invariants.
//包内私有的构造器
Buffer(int mark, int pos, int lim, int cap) { // package-private
//缓冲区容量小于0时抛出异常
if (cap < 0)
throw new IllegalArgumentException("Negative capacity: " + cap);
//根据传参设置缓冲区大小
this.capacity = cap;
//设置limit
limit(lim);
//设置position
position(pos);
if (mark >= 0) {
if (mark > pos)
//mark大于pos并且mark大于等于0时会抛出异常
throw new IllegalArgumentException("mark > position: ("
+ mark + " > " + pos + ")");
//设置mark
this.mark = mark;
}
}
常用方法
capacity
capacity()方法用于返回缓冲区的容量,即缓冲区中可以存储的元素的最大数量。
/**
* Returns this buffer's capacity.
*
* @return The capacity of this buffer
* 返回缓冲区容量
*/
public final int capacity() {
return capacity;
}
position
用于获取缓冲区的当前位置。在缓冲区中,位置是指下一个要被读取或写入的元素的索引,初始位置为0。通过调用position()方法,可以获取当前位置,也可以将当前位置设置为指定值。
/**
* Returns this buffer's position.
*
* @return The position of this buffer
* 返回缓冲区下一次要读取或者写入的数组下标
*/
public final int position() {
return position;
}
/**
* Sets this buffer's position. If the mark is defined and larger than the
* new position then it is discarded.
*
* @param newPosition
* The new position value; must be non-negative
* and no larger than the current limit
*
* @return This buffer
*
* @throws IllegalArgumentException
* If the preconditions on <tt>newPosition</tt> do not hold
* 设置缓冲区的游标
*/
public final Buffer position(int newPosition) {
//要设置的position值不能大于limit值或者小于0,否则就抛出异常。
if ((newPosition > limit) || (newPosition < 0))
throw createPositionException(newPosition);
//如果设置的新的游标值比mark值小,就重置mark值为-1.
if (mark > newPosition) mark = -1;
//设置游标
position = newPosition;
return this;
}
limit
用于设置缓冲区的限制(limit),调用limit方法后,缓冲区的限制将被设置为指定的值。如果缓冲区的当前位置大于新的限制值,则当前位置将被设置为新的限制值。
/**
* Returns this buffer's limit.
*
* @return The limit of this buffer
* 返回缓冲区的可用数据长度。
*/
public final int limit() {
return limit;
}
/**
* Sets this buffer's limit. If the position is larger than the new limit
* then it is set to the new limit. If the mark is defined and larger than
* the new limit then it is discarded.
*
* @param newLimit
* The new limit value; must be non-negative
* and no larger than this buffer's capacity
*
* @return This buffer
*
* @throws IllegalArgumentException
* If the preconditions on <tt>newLimit</tt> do not hold
* 设置缓冲区的limit值,同时会保证position和mark值保证这几个值的关系符合规则。
*/
public final Buffer limit(int newLimit) {
//对要设置的新limit进行检查,如果大于缓冲区容量或者小于0就抛出异常
if ((newLimit > capacity) || (newLimit < 0))
throw new IllegalArgumentException();
//设置limit
limit = newLimit;
//如果游标比可操作的最大数组下标还大的话,就把游标设置为limit
if (position > newLimit) position = newLimit;
//如果mark > limit,重置mark。
if (mark > newLimit) mark = -1;
return this;
}
mark
mark方法会将一个标记设置在当前位置,以便在后续的操作中可以返回到这个位置。这个标记可以通过调用reset方法返回。
/**
* Sets this buffer's mark at its position.
*
* @return This buffer
* 标记当前position位置。
*/
public final Buffer mark() {
mark = position;
return this;
}
reset
用于重置缓冲区,即清空缓冲区并重新设置缓冲区的初始状态。
/**
* Resets this buffer's position to the previously-marked position.
*
* <p> Invoking this method neither changes nor discards the mark's
* value. </p>
*
* @return This buffer
*
* @throws InvalidMarkException
* If the mark has not been set
* 把游标恢复到标记的位置
*/
public final Buffer reset() {
int m = mark;
//标记小于0时抛出异常
if (m < 0)
throw new InvalidMarkException();
//重置游标至标记处
position = m;
return this;
}
clear
用于重置缓冲区的位置、极限和标记。它将position设置为0,将标记设置为-1,并将极限设置为容量。此方法不会清除缓冲区中的数据。调用该方法后,您可以重新填充缓冲区或读取缓冲区中的现有数据。
/**
* Clears this buffer. The position is set to zero, the limit is set to
* the capacity, and the mark is discarded.
*
* <p> Invoke this method before using a sequence of channel-read or
* <i>put</i> operations to fill this buffer. For example:
*
* <blockquote><pre>
* buf.clear(); // Prepare buffer for reading
* in.read(buf); // Read data</pre></blockquote>
*
* <p> This method does not actually erase the data in the buffer, but it
* is named as if it did because it will most often be used in situations
* in which that might as well be the case. </p>
*
* @return This buffer
* 清空缓冲区,其实就设置游标为0,limit设置回capacity值,mark重置,数据还是存在的。
*/
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
flip
flip方法是用于将写模式下的缓冲区切换为读模式。在写模式下,缓冲区的指针指向缓冲区的末尾,而在读模式下,指针指向缓冲区的开头。当需要读取已写入的数据时,需要使用flip方法切换模式。
/**
* Flips this buffer. The limit is set to the current position and then
* the position is set to zero. If the mark is defined then it is
* discarded.
*
* <p> After a sequence of channel-read or <i>put</i> operations, invoke
* this method to prepare for a sequence of channel-write or relative
* <i>get</i> operations. For example:
*
* <blockquote><pre>
* 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</pre></blockquote>
*
* <p> This method is often used in conjunction with the {@link
* java.nio.ByteBuffer#compact compact} method when transferring data from
* one place to another. </p>
*
* @return This buffer
* 缓冲区创建时默认是写模式的,这个方法把缓冲区改为读模式。
* 每次通过通道往存储设备中写数据都需要调用此方法把缓冲区设置为读模式。读取缓冲区的数据。
*/
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
rewind
将缓冲区的position重置为0,表示可以重新读取缓冲区的内容。此方法不会改变缓冲区的limit和capacity。
/**
* Rewinds this buffer. The position is set to zero and the mark is
* discarded.
*
* <p> Invoke this method before a sequence of channel-write or <i>get</i>
* operations, assuming that the limit has already been set
* appropriately. For example:
*
* <blockquote><pre>
* out.write(buf); // Write remaining data
* buf.rewind(); // Rewind buffer
* buf.get(array); // Copy data into array</pre></blockquote>
*
* @return This buffer
* 重置游标,从新开始读、写数据。
*/
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
remaining
用于计算缓冲区中还剩余的未读元素数量。具体来说,remaining()方法返回值是一个int类型的整数,表示当前缓冲区中还有多少个元素可以被读取。
/**
* Returns the number of elements between the current position and the
* limit.
*
* @return The number of elements remaining in this buffer
* 读模式下,返回剩余可读的数据长度,写模式下,返回剩余可写的缓冲区长度。
*/
public final int remaining() {
int rem = limit - position;
return rem > 0 ? rem : 0;
}
/**
* Tells whether there are any elements between the current position and
* the limit.
*
* @return <tt>true</tt> if, and only if, there is at least one element
* remaining in this buffer
* 返回是否还有数据可读或者可写。
*/
public final boolean hasRemaining() {
return position < limit;
}
nextGetIndex
用于获取下一个get(读取)操作的位置。读取操作和写入操作都是在当前位置(position)进行的。nextGetIndex方法返回的是当前位置加上1,即下一个读取操作应该从哪个位置开始读取数据。如果当前位置已经是buffer的limit值,那么下一个读取操作将无法进行,因为读取操作不能超过limit。
/**
* Checks the current position against the limit, throwing a {@link
* BufferUnderflowException} if it is not smaller than the limit, and then
* increments the position.
*
* @return The current position value, before it is incremented
* 读模式下游标往右移一位,也就是跳过一个数据。
* 返回移动前的游标值。
*/
final int nextGetIndex() { // package-private
int p = position;
//因为游标加一 所以要确保游标加一前要小于等于limit
if (p >= limit)
//当前游标>=limit抛出异常
throw new BufferUnderflowException();
position = p + 1;
return p;
}
/**
* 读模式下游标往右移动n位。
* 返回移动前的游标
* @param nb
* @return
*/
final int nextGetIndex(int nb) { // package-private
int p = position;
if (limit - p < nb)
//判断加n后的游标是否大于limit,大于就抛出异常
throw new BufferUnderflowException();
position = p + nb;
//返回移动前的游标。
return p;
}
nextPutIndex
用于返回下一个可写入的位置的索引。该方法会检查缓冲区的容量以确保缓冲区已分配足够的空间来写入数据。如果缓冲区容量不足,则会调用expand()方法来扩展缓冲区大小
/**
* Checks the current position against the limit, throwing a {@link
* BufferOverflowException} if it is not smaller than the limit, and then
* increments the position.
*
* @return The current position value, before it is incremented
* 写模式下游标往右移动1位。
* 返回移动前的游标
*/
final int nextPutIndex() { // package-private
int p = position;
if (p >= limit)
throw new BufferOverflowException();
position = p + 1;
return p;
}
/**
* 写模式下游标往右移动n位。
* 返回移动前的游标
* @param nb
* @return
*/
final int nextPutIndex(int nb) { // package-private
int p = position;
if (limit - p < nb)
throw new BufferOverflowException();
position = p + nb;
return p;
}