盘一盘 NIO (一)—— Buffer源码解析
Buffer是个啥?
Buffer 即缓冲区,用来暂存输入输出数据的区域。Buffer对象是一份固定数量的数据的容器,实质上是一个数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,还可以跟踪系统的读/写进程。
在 Java传统 IO 中,数据直接写入或者将数据直接读到 Stream 对象中。
而在 NIO 中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,都是将它放到缓冲区中。
NIO中的Buffer主要用于与NIO通道(channel)进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
继承关系图
Buffer作为一个容器用来存储数据的,根据存储数据类型的不同,分为以下七种:
ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer
主要属性
// 标记、位置、限制、容量遵守以下不变式: // 0<=mark<=position<=limit<=capacity // 标记是一个位置索引,可以在之后设置位置为标记值再次读写。 private int mark = -1; // 位置,表示缓冲区中正在操作数据的位置。 // 可以把position理解成一个指针,指向的位置就是操作的位置。 private int position = 0; // 界限,表示缓冲区中可以操作数据的大小。 private int limit; // 容量,表示缓冲区中最大存储数据的容量。一旦声明,不可改变。 private int capacity; // 指向缓冲区的地址 long address;
构造方法
// 创建一个具有给定标记、位置、限制和容量的新缓冲区 Buffer(int mark, int pos, int lim, int cap) { // 缓冲区的初始化容量不能小于0 if (cap < 0) throw new IllegalArgumentException("Negative capacity: " + cap); // 初始化缓冲区容量 this.capacity = cap; // 初始化缓冲区限制,设置最新的缓冲区Buffer limit(lim); position(pos); if (mark >= 0) { if (mark > pos) throw new IllegalArgumentException("mark > position: (" + mark + " > " + pos + ")"); this.mark = mark; } }
关键方法解析
position方法:设置当前操作位置
public final Buffer position(int newPosition) { // 当前操作位置不能超过limit大小,不能小于0 if ((newPosition > limit) || (newPosition < 0)) throw new IllegalArgumentException(); position = newPosition; // 如果标记位置大于正在操作位置,那么将mark设置为-1。 if (mark > position) mark = -1; return this; }
limit方法:设置缓冲区限制
public final Buffer limit(int newLimit) { // newLimit大小不能超过缓冲区最大容量capacity,不能小于0 if ((newLimit > capacity) || (newLimit < 0)) throw new IllegalArgumentException(); limit = newLimit; // 正在操作数据的位置不能大于界限 if (position > limit) position = limit; // 如果标记位置大于界限,那么将mark设置为-1。 if (mark > limit) mark = -1; return this; }
reset方法:将缓冲区Buffer的位置position设置为以前临时备忘变量mark存储的位置
public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this; }
clear方法:清除缓冲区,clear方法并没有将之前的数据真的清除,当读数据前调用flip方法后,limit会限制不让使用旧数据。
public final Buffer clear() { position = 0; // 虽然将limit数值设置为最大容量,只是为了正常使用buffer limit = capacity; mark = -1; return this; }
flip方法: 反转此缓冲区,在进行写完去读的时候使用,说白了,让你开始读的时候从初始位置0开始。
public final Buffer flip() { // 将position置为0,其实就是切换读写模式 limit = position; position = 0; mark = -1; return this; }
rewind方法:重绕此缓冲区,在通道写入或者获取之前调用
public final Buffer rewind() { position = 0; mark = -1; return this; }
remaining方法:返回当前的缓冲区Buffer中剩余元素的数量
public final int remaining() { return limit - position; }
总结
1、Buffer中所有的方法操作,都遵守以下不变式:
0<=mark<=position<=limit<=capacity
2、flip方法,让数据开始读的时候从初始位置0开始。
3、clear方法其实是个障眼法,并没有清除之前数据内容。