下面通过分析java nio的Buffer类的源代码来学习Buffer中的position、limit、capcity和marker几个标志位的变化。源代码分析如下:
public abstract class Buffer { // 变量的取值约束: mark <= position <= limit <= capacity private int mark = -1; // 标记 private int position = 0; // 当前位置 private int limit; // 限制 private int capacity; // 容量 …… }
上面是Buffer.java类定义的变量,其中这些变量保存了缓冲区的状态。
Buffer类的构造方法,代码如下:
// 使用指定参数创建一个Buffer实例 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(lim); position(pos); // 标记必须大于等于0且小于当前位置 if (mark >= 0) { if (mark > pos) throw new IllegalArgumentException("mark > position: (" + mark + " > " + pos + ")"); this.mark = mark; } }
这个构造方法中设置了缓冲区的容量、限制、当前位置和标记。
当Buffer接收用户写入数据时,默认的状态是position=1,capacity=limit。如下图:
向缓冲区中输入数据,此时缓冲区中的position状态会发生改变,每多输入一个字节position就会自动加1,如下图是缓冲区写入一部分数据后,缓冲区的状态。
当数据输入完毕(指缓冲区已满或数据已经输入完),此时需要调用flip方法将缓冲区进行反转,即设置当前位置position=0,限制limit等于position(缓冲区数据容量),源码如下:
// 反转缓冲区,即为读取数据做好准备(设置标志) // 将限制设置为当前位置,再将当前位置设置为缓冲区开始0,清除标记。 public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
下图为从缓冲区中读入一部分数据后的状态图:
使用mark方法对已经读入的数据进行标记,标记后你可以通过reset方法重置到mark标记处,下面是mark的源码:
// 设置标记为当前位置 public final Buffer mark() { mark = position; return this; }
上图是设置标记后,继续读取一部分数据后缓冲区的状态。设置标记的目的就是在以后可以返回到标记处重新读取标记后面的数据,要想将当前位置推回到标记处,只需要调用reset方法,源代码如下:
// 将当前位置设置为标记值 public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this; }
重置后的缓冲区状态图: