系列第二篇,深入剖析 Buffer 核心狀態變量(position、limit、capacity、mark)與關鍵操作 clear()、flip()、rewind()、compact() 的內部邏輯,透過狀態機圖解讓你徹底掌握「讀寫切換」本質。
Buffer 核心四大屬性
public abstract class Buffer {
// 0 <= mark <= position <= limit <= capacity
private int mark = -1; // 標記位置,reset() 回退用
private int position = 0; // 下一次讀/寫位置
private int limit; // 邊界:還能讀/寫多少
private int capacity; // 固定容量
}
狀態機圖解:寫模式 → 讀模式 → 寫模式
初始狀態(分配後)
capacity=8, limit=8, position=0, mark=-1
[ ][ ][ ][ ][ ][ ][ ][ ]
^ ^
pos=0 limit=8
寫入 3 個元素後
position=3, limit=8
[ A ][ B ][ C ][ ][ ][ ][ ][ ]
^ ^
pos=3 limit=8
執行 flip():切換到讀模式
public final Buffer flip() {
limit = position; // 讀取邊界 = 已寫入數量
position = 0; // 從頭讀
mark = -1;
return this;
}
limit=3, position=0
[ A ][ B ][ C ][ ][ ][ ][ ][ ]
^ ^
pos=0 limit=3
讀取 2 個元素後
position=2, limit=3
[ A ][ B ][ C ][ ][ ][ ][ ][ ]
^ ^
pos=2 limit=3
執行 clear():切換到寫模式(清空狀態,數據不清零)
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
position=0, limit=8 (capacity)
[ A ][ B ][ C ][ ][ ][ ][ ][ ]
^ ^
pos=0 limit=8
clear() 不清零數據,僅重置位置標記,舊數據會被後續寫入覆蓋。
四大核心操作對照表
| 操作 | 適用場景 | position | limit | mark | 語義 |
| flip() | 寫完 → 讀取 | =0 | =position | =-1 | 鎖定已寫數據範圍,準備讀取 |
| clear() | 讀完 → 重寫 | =0 | =capacity | =-1 | 準備覆寫整個緩衝區 |
| rewind() | 重新讀取 | =0 | 不變 | =-1 | position 歸零,limit 不變(重讀已讀數據) |
| compact() | 未讀完 → 續寫 | =剩餘數據量 | =capacity | =-1 | 未讀數據移至頭部,position 接在後面續寫 |
compact() 圖解:未讀完續寫
讀取 2 個後:position=2, limit=3
[ A ][ B ][ C ][ ][ ][ ][ ][ ]
^ ^
compact() 後:
[ C ][ ][ ][ ][ ][ ][ ][ ]
^ ^
pos=1 limit=8
// C 移至頭部,position=1 可續寫
mark()/reset():標記與回退
buffer.mark(); // mark = position
// ... 讀取若干 ...
buffer.reset(); // position = mark (回退到標記處)
僅支援單一標記,巢狀調用會覆蓋。
實戰:通道間高效傳輸循環
ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 64); // 直接緩衝區,零拷貝
while (inChannel.read(buf) != -1) {
buf.flip(); // 切換讀模式
outChannel.write(buf); // 寫出(write 會更新 position)
buf.compact(); // 未寫完數據移頭部,續讀
}
buf.flip();
while (buf.hasRemaining()) outChannel.write(buf); // 刷新殘留
直接緩衝區 vs 堆緩衝區