Buffers阅读笔记
文章目录
一、简介
这是一个缓存区的抽象类,真正的数组存储在ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、CharBuffer、DoubleBuffer、FloatBuffer
二、继承关系图
- 由上图(可以放大)可以看出,Buffer是一个抽象类,有很多子类去实现,但是他们也都是抽象类,但是最终实现的是HeapxxxBuffer。Byte开头的因为特殊处理字节,所以会有一个Bits协助
三、存储结构
- 自身没有存储结构,由抽象子类xxxBuffer有一个存储数组(同类型)
四、源码分析
内部类
- 无
属性
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;// 标记,用于reset把position修改为mark,如果mark=-1或未定义会抛异常
private int position = 0;// 当前位置,表示读取或写入的位置
private int limit;// 读取/写入的限制,当前索引开始及以后不可读取和写入
private int capacity;// 容量。代表该缓存中的容量
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
long address;
构造
// 初始化构造器,同时初始化核心的4个属性,注意此构造是采用的默认修饰,所以是不支持直接实例化
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;
}
}
主要方法
1、读取4大属性
-
读取 4 大属性
public final int capacity() {return capacity;} public final int position() {return position;} public final int limit() {return limit;} // package-private,仅支持同包及本类访问 final int markValue() {return mark;}
2、单个操作4大属性
-
单个操作 4 大属性
/** * capacity 仅在实例化的时候设置 */ /** * 设置position,涉及到逻辑处理 */ public final Buffer position(int newPosition) { // 边界效验 if ((newPosition > limit) || (newPosition < 0)) throw new IllegalArgumentException(); // 设置 position = newPosition; // 1. mark 大于 position 直接设置为-1,reset()就会抛出异常(mark小于0) if (mark > position) mark = -1; return this; } /** * 设置limit,涉及到逻辑处理 */ public final Buffer limit(int newLimit) { // 边界效验 if ((newLimit > capacity) || (newLimit < 0)) throw new IllegalArgumentException(); // 设置 limit = newLimit; // 1. position 大于limit,则直接设置position为limit // 也代表读写操作都会抛出异常 if (position > limit) position = limit; // 2. mark 大于 limit直接设置为-1,reset()就会抛出异常(mark小于0) if (mark > limit) mark = -1; return this; } /** * 设置mark,在当前位置(position)设置标记 */ public final Buffer mark() { mark = position; return this; }
3、缓冲区4属性处理
-
缓冲区属性处理
/** * 还原缓冲区的数据 * 还原一切状态 */ public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; } /** * 设置当前当前位置重置到标记位置 */ public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this; } /** * 反转此缓冲区 * 适合用于在缓冲区写入数据,然后,再从缓冲区读取数据(不用考虑limit限制) * 也就是使缓冲区为“重新读取”已包含的数据做好准备,他使限制保持不变,讲位置设置为0 * 类似substring截取 */ public final Buffer flip() { limit = position; position = 0; mark = -1; return this; } /** * 重绕缓冲区,将当前位置设置为0,并丢弃标记 * 适合用于重新读取/写入缓冲区时,(前提是设置了适当的limit限制) * 重新读取重新吸入的时候可用 */ public final Buffer rewind() { position = 0; mark = -1; return this; }
4、判断剩余量和底层数组
-
判断剩余量和底层数组
/** * 判断是否还有剩余可读写容量 */ public final boolean hasRemaining() { return position < limit; } /** * 抽象方法:是否是只读(非直接缓存(JVM中间缓存区)支持读写,但是直接缓存(内存)只支持读) */ public abstract boolean isReadOnly(); /** * 抽象方法:JVM缓冲区是否有数组 */ public abstract boolean hasArray(); /** * 抽象方法:是否是直接缓存区(直接跳过JVM中间缓存区) */ public abstract boolean isDirect();
5、获取剩余容量和底层数组
-
获取剩余容量和底层数组
/** * 获得剩余可读写的容量 */ public final int remaining() { return limit - position; } /** * 抽象方法,获取数组 */ public abstract Object array(); /** * 获取数组的偏移量 */ public abstract int arrayOffset();
6、其他package-private可用方法
-
其他默认修饰方法
/** * 所有子类get使用,使当前位置(position)+ 1,并返回原position索引 */ final int nextGetIndex() { // package-private if (position >= limit) throw new BufferUnderflowException(); return position++; } /** * 所有子类get使用,使当前位置(position)+ nb,并返回原position索引 */ final int nextGetIndex(int nb) { // package-private if (limit - position < nb) throw new BufferUnderflowException(); int p = position; position += nb; return p; } /** * 同get一样逻辑 * 所有子类put使用,使当前位置(position)+ 1,并返回原position索引 */ final int nextPutIndex() { // package-private if (position >= limit) throw new BufferOverflowException(); return position++; } /** * 同get一样逻辑 * 所有子类put使用,使当前位置(position)+ nb,并返回原position索引 */ final int nextPutIndex(int nb) { // package-private if (limit - position < nb) throw new BufferOverflowException(); int p = position; position += nb; return p; } /** * 检查 i 是否可读写 */ final int checkIndex(int i) { // package-private if ((i < 0) || (i >= limit)) throw new IndexOutOfBoundsException(); return i; } /** * 检查 i 是否可读写,nb代表每次读写的位,一次读写的位超出limit则边界异常 */ final int checkIndex(int i, int nb) { // package-private if ((i < 0) || (nb > limit - i)) throw new IndexOutOfBoundsException(); return i; } /** * 读取当前mark值 */ final int markValue() { // package-private return mark; } /** * 限制读写操作,直接所有设置最低值 */ final void truncate() { // package-private mark = -1; position = 0; limit = 0; capacity = 0; } /** * 丢弃原mark,直接设置为-1 */ final void discardMark() { // package-private mark = -1; } /** * off 开始索引位 * len 每次读写操作量 * size 此数组容量长度 * 效验本次读取是否超出边界 */ static void checkBounds(int off, int len, int size) { // package-private if ((off | len | (off + len) | (size - (off + len))) < 0) throw new IndexOutOfBoundsException(); }
补充
五、总结
抽象类 ShortBuffer、LongBuffer、IntBuffer、DoubleBuffer、FloatBuffer的所有函数基本上是一样的,只是存储的接口不同。所以操作有点却别,但是函数一样。
charBuffer由于需要操作字符,所以比上面的会多一些例如append,charAt、subDequence函数
ByteBuffer因为也比上面多,例如_get、_put、putXXX、getXXX、asXXXBuffer等
heapXXXBuffer的实现类同上,也是相似和不同之处。原因请看后面的源码分析