abstract class Buffer
7个直接子类
abstract class ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer。
NIO中的Buffer是一个用于存储脚本数据类型的容器,以类似于数组有序的方式来存储和组织数据。
//Invariants: mark <= position <= limit <= capacity
private int mark = -1; //标记
private int position = 0; //位置
private int limit; //限制
private int capacity; //容量
Buffer的7个子类也是抽象的,不能通过new 实例化,使用静态方法wrap()实现。wrap方法将数组放入缓冲区,来构建存储不同数据类型的缓冲区。
byte[] byteArray = new byte[]{1,2,3};
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
System.out.println(byteBuffer.getClass().getName());
System.out.println(byteBuffer.capacity());
output:
java.nio.HeapByteBuffer
3
wrap源码
/**
* Wraps a byte array into a buffer.
*
* The new buffer will be backed by the given byte array;
* that is, modifications to the buffer will cause the array to be modified
* and vice versa. The new buffer's capacity and limit will be
* array.length, its position will be zero, and its mark will be
* undefined. Its {@link #array backing array} will be the
* given array, and its {@link #arrayOffset array offset>} will
* be zero.
……
*/
public static ByteBuffer wrap(byte[] array) {
return wrap(array, 0, array.length);
}
/**……
*
* @param array
* The array that will back the new buffer
* @param offset
* The offset of the subarray to be used; must be non-negative and
* no larger than array.length. The new buffer's position
* will be set to this value.
* @param length
* The length of the subarray to be used;
* must be non-negative and no larger than
* array.length - offset.
* The new buffer's limit will be set to offset + length.
* @return The new byte buffer
*/
public static ByteBuffer wrap(byte[] array,int offset, int length){
try {
return new HeapByteBuffer(array, offset, length);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}
}
ByteBuffer类缓冲区的原理是使用byte[]数组进行数据的保存,在后续使用指定的API来操作这个数组已达到操作缓冲区的目的。
HeapByteBuffer(byte[] buf, int off, int len) { // package-private
super(-1, off, off + len, buf.length, buf, 0);
}
ByteBuffer(int mark, int pos, int lim, int cap, byte[] hb, int offset){
super(mark, pos, lim, cap);
this.hb = hb;
this.offset = offset;
}
从源码可以看到,缓冲区存储的数据还是存储在byte[]字节数组中。使用缓冲区与使用byte[]字节数组的优点在于缓冲区将存储数据的byte[]字节数组内容与相关的信息整合在一个Buffer类中,将数据与缓冲区中的信息进行了整合并封装,便于获得相关的信息及处理数据。
限制代表第一个不应该读取或写入元素的index
limit源码:
/**
* Returns this buffer's limit.
*/
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 newLimit do not hold
*/
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;
}
示例
char[] charArr = new char[]{'a','b','c','d','e','f'};
CharBuffer charBuffer = CharBuffer.wrap(charArr);
System.out.println("capacity = " + charBuffer.capacity() + " limit=" + charBuffer.limit());
charBuffer.limit(3);
System.out.println("capacity = " + charBuffer.capacity() + " limit=" + charBuffer.limit());
charBuffer.put(0,'h');
charBuffer.put(1,'i');
charBuffer.put(2,'j');
charBuffer.put(3,'k'); //第一个不可读不可写的索引
charBuffer.put(4,'l');
output:
capacity = 6 limit=6
capacity = 6 limit=3
java.lang.IndexOutOfBoundsException
……
Limit的使用场景是当反复向缓冲区中存储数据时使用,如第一次向缓冲区写入 A到G 7个字符,然后全部读取,第二次向缓冲区写入1到4 4个字符,如果读取到1、2、3、4、E、F、G是错误的,需要结合limit限制读取范围。
源码
/**
* 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 newPosition do not hold
*/
public final Buffer position(int newPosition) {
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
if (mark > position) mark = -1;
return this;
}
返回当前位置与limit之间的元素数
/**
* 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() {
return limit - position;
}
```
## 使用Buffer mark()方法处理标记
缓冲区的 标记是一个索引,在调用reset()方法时,会将缓冲区的position位置重置为该索引。标记mark并不是必须的。定义mark时,不能将其定义为负数,并且不能让他大于position。如果定义了mark,则在将position或limit调整为小于该mark的值时,该mark将被丢弃,丢弃后的mark值是-1.如果未定义mark,那么调用reset方法将导致抛出invalidMarkException。