一 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 使用前必須指定大小,這個空間後期不會改變,是設計的一方面的缺陷吧。