一 NIO 是非阻塞,主要是Channel , Selector, Buffer ,我们来学习下Buffer,此图不全。
发现buffer,java 实现了除了boolean 类型的其他的7中基础数据类型。
Buffer 是指一个存放基元类型的数据容器。
如何定义一个Buffer。
从构造函数的参数可以看出,需要传递4个参数。
mark:标记的位置,重新标记读写的position位置。
pos: 缓存下一个将要读取或或写的位置。
lim: 限制写入或读取的长度大小
cap: 表示初始化后buffer对象的长度。
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;
}
}
在构造函数中对lim / pos 进行了预判。
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;
}
首先 limit 限制必须满足小于capacity的限制,而且不能为负数,不然会抛出异常。
如果当前的position大于limit的限制,则把position值进行负值给limit。
前文说了mark是标记position的位置的,如果mark大于limit的话,怎丢弃标志mark,-1 表示丢弃。
public final Buffer position(int newPosition) {
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
if (mark > position) mark = -1;
return this;
}
对position位置的检测,position 不能大于limit ,而且不能为负数,否则抛出异常。
同时mark如果大于position了,则被丢弃。这里你是否有疑问,mark的值是有position复值,怎么会和position不一致呢。查看 源码,代码里确实如此,不过初始化的时候,需要做mark校验检查的。
public final Buffer mark() {
mark = position;
return this;
}
-1 表示丢弃?
final void discardMark() { // package-private
mark = -1;
}
buffer的作用是用来进行读写数据,数据不是write 就 read.
/** <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>
*/
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
/** <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;
}
这个两个方法都可以用与buffer的读取状态切换,哪有什么区别的,flip() 比 rewind 多了一行代码。
rewind 每次操作都是buffer的全部数据假设limit=cap。 而flip是有限制的。
还有一写辅助操作buffer 的辅助方法。
剩余未操作的数据大小
public final int remaining() {
return limit - position;
}
判断是否可操作
public final boolean hasRemaining() {
return position < limit;
}
清除并不是真正的清除数据,是改变position值和限制大小,如何避免多读取数据呢,关键在于limit 的值=position,如果数据操作了。就做限制,读取数据就不会出错。
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;
}
重设只是改变position的值设置为mark,在mark位置进行重新的操作。
以上的操作并没有真正清除数据,只是进行数据的覆盖。
边界检查
static void checkBounds(int off, int len, int size) { // package-private
if ((off | len | (off + len) | (size - (off + len))) < 0)
throw new IndexOutOfBoundsException();
}
off: 开始索引
len:操作的数组长度
size: 数组的大小
'||' 表示只要一个满足都满足,不再进行其他检查判断。
‘|’ 表示按位或, 负数|任何数都是负数。
总结:
Buffer 是 NIO出现的类,在做数据的序列化 传输上使用。
Buffer 使用前必须指定大小,这个空间后期不会改变,是设计的一方面的缺陷吧。