本篇是有關 Java NIO 的 Buffer 類別,請先參考先前的文章,先認識 Java NIO:
Java的NIO,什麼是blocking? 什麼是non-blocking?
緩衝區Buffer
Buffer 是 NIO 的一個重要的類別,其角色是介於遠端資料來源端與程式之間的「緩衝區」,當從 Channel 讀取資料時,先將資料置於 Buffer 中,程式再從 Buffer 中讀取資料。如下圖:
java.nio.Buffer 是個抽象類別,它制定了緩衝區該有的屬性與方法,並成為許多各類型緩衝區類別的父類別,例如專門用來存放位元組的 ByteBuffer、存放字元的 CharBuffer 等,常用的緩衝區類別有七種,分別對應除了布林值外的七個基本資料型別。
緩衝區的分類
緩衝區有「直接緩衝區」與「間接緩衝區」兩大類,直接緩衝區可跳過 JVM 的處理,直接對應到作業系統的記憶體來存取緩衝區資料,適合較大檔案或需要長存活時間的需求,因為 JVM 通常會因為本身被限制的記憶體使用量,而影響緩衝區的使用量。而間接緩衝區則是由 JVM 負責所有的容量分配與存取動作,使用上會耗費 JVM 本身所分配的記憶體,相對的比直接緩衝區的限制要來得多。
在實務的設計中,除非要存取的檔案較大,否則皆不採用直接緩衝區,因為在取得或清除該緩衝區時會花費較多的時間。通常都採用間接緩衝區來處理,產生間接緩衝區的方式是呼叫 Buffer 的靜態方法 allocate 來得到物件,例如要得到一個可容納 10 個 byte 的緩衝區物件:
ByteBuffer buf = ByteBuffer.allocate(20);
或
CharBuffer cbuf = CharBuffer.allocate(20);
要得到直接緩衝區的物件時,則要呼叫 ByteBuffer 的靜態方法 allocatDirect :
ByteBuffer buf = ByteBuffer.allocateDirect(20);
而且只能由 ByteBuffer 產生直接緩衝區(只有 ByteBuffer 有 allocateDirect 方法),再由 ByteBuffer 進行轉型得到其他類型的緩衝區,且 Buffer 皆可使用 isDirect() 得到目前的緩衝區是否為直接緩衝區:
import java.nio.ByteBuffer; import java.nio.CharBuffer; public class BufferDirect { public static void main(String[] args) { //間接緩衝區 ByteBuffer buf = ByteBuffer.allocate(20); System.out.println("是否是直接緩衝區?"+buf.isDirect()); CharBuffer cbuf = CharBuffer.allocate(20); System.out.println("是否是直接緩衝區?"+cbuf.isDirect()); //直接緩衝區 ByteBuffer dbuf = ByteBuffer.allocateDirect(20); System.out.println("是否是直接緩衝區?"+dbuf.isDirect()); } }
程式執行結果:
是否是直接緩衝區?false 是否是直接緩衝區?false 是否是直接緩衝區?true
產生ByteBuffer
產生 ByteBuffer 有三種方式,除了上述呼叫 ByteBuffer 類別的靜態方法 allocate 以外,還可以由已存在的 byte 陣列「包裝(wrap)」而得到,另外使用 FileChannel 的 map 方法,則可將檔案資料對應成為緩衝區。
將資料放入緩衝區
準備好緩衝區物件後,可使用 put 方法將資料放入緩衝區,各個 put 方法的使用說明如下:
-
ByteBuffer put(byte[] src)
將一個 byte 陣列的資料放入緩衝區,並回傳該緩衝區,例如:
ByteBuffer buf = ByteBuffer.allocate(10); byte[] bb = "abcxyz".getBytes(); buf.put(bb);
buf的內容將成為:
-
ByteBuffer put(byte[] src, int offset, int length)
將 byte 陣列中特定範圍的元素放入緩衝區,從陣列的 offset 位置開始取得 length 個元素,並回傳該緩衝區,例如取得 “abcxyz” 字串中的 “yz”,並放入 ByteBuffer 中:
ByteBuffer buf = ByteBuffer.allocate(10); byte[] bb = "abcxyz".getBytes(); buf.put(bb, 4, 2);
buf 的內容將成為:
-
ByteBuffer put(ByteBuffer src)
將另一個 ByteBuffer 的內容放到目前的 ByteBuffer 中,回傳該緩衝區,例如將一個緩衝區 buf 的內容放到另一個 buf2 中:
ByteBuffer buf = ByteBuffer.allocate(10); byte[] bb = "abcxyz".getBytes(); buf.put(bb); ByteBuffer buf2 = ByteBuffer.allocate(10); buf2.put(buf);
buf2 的內容將成為: