前面介紹了文件通道的讀寫操做,其中用到字節緩存ByteBuffer,它是位於通道內部的存儲空間,也是通道惟一可用的存儲形式。ByteBuffer有兩種構建方式,一種是調用靜態方法wrap,根據輸入的字節數組生成對應的緩存對象;另外一種是調用靜態方法allocateDirect,根據輸入的數值分配指定大小的空緩存。字節緩存又是一種特殊的存儲空間,由於它可能會被屢次讀寫,因此爲了有效地控制讀寫操做,Java給它設計了下列五種概念:容量、當前限制量、當前位置、本次剩餘空間、標記位置,分別說明以下:
一、容量(capacity):指的是字節緩存的整個長度。容量大小可經過緩存對象的capacity方法得到。
二、當前限制量(limit):指的是當前讀寫操做所能處理的最大空間大小。當前限制量可經過緩存對象的limit方法得到(不帶輸入參數),攜帶輸入參數的limit方法用來設置當前限制量的數值。若是不設置當前限制量的大小,則limit數值默認爲字節緩存的容量大小。
三、當前位置(position):指的是字節緩存當前操做的起始位置。當前位置可經過緩存對象的position方法得到(不帶輸入參數),攜帶輸入參數的position方法用來設置當前位置的數值。字節緩存一開始的當前位置是0,每次進行讀寫操做位置以後,當前位置都會日後跟着挪動。
四、本次剩餘空間(remaining):它的數值等於當前限制量減去當前位置(即limit-position)。本次剩餘空間可經過緩存對象的remaining方法得到。
五、標記位置(mark):其概念相似緩存輸入流的標記,一樣是調用mark方法在當前位置作個標記,以便後續調用reset方法可以回到上次標記的位置。
舉個例子,如今分配了一個容量大小爲10的字節緩存,而且設置它的當前限制量爲8,接着將當前位置移到第三個字節處(下標爲2),那麼該字節緩存的存儲結構應當以下圖所示。html
搞清楚了字節緩存的內部結構,再來看與字節緩存有關的數據流向。字節緩存與磁盤文件之間經過文件通道FileChannel交互,與內存字符串之間經過字節數組byte[]交互,因而內存中的一個字符串想要與磁盤上的某個文件內容相互轉換的話,就存在如下兩種數據流轉過程:
一、把字符串寫入文件,此時數據流向爲:字符串String→字節數組byte[]→字節緩存ByteBuffer→指定路徑的文件。
二、把文件內容讀到字符串,此時數據流向爲:指定路徑的文件→字節緩存ByteBuffer→字節數組byte[]→字符串String。
其中與字節緩存有關的讀寫操做又可拆分爲下列四種方法調用:
一、字節數組byte[]→字節緩存ByteBuffer,該操做除了調用ByteBuffer的靜態方法wrap以外,還能經過緩存對象的put方法往字節緩存寫入字節數組。
二、字節緩存ByteBuffer→指定路徑的文件,該操做須要調用通道對象的write方法,往磁盤文件寫入字節緩存中的數據。
三、指定路徑的文件→字節緩存ByteBuffer,該操做須要調用通道對象的read方法,把磁盤文件中的數據讀到字節緩存。
四、字節緩存ByteBuffer→字節數組byte[],該操做須要經過緩存對象的get方法,把字節緩存中的數據取到字節數組。
詳細的數據流轉過程可見下圖,其中動做①和動做②實現了將字符串寫入文件的功能,動做③和動做④實現了將文件內容讀到字符串的功能。數組
注意到上圖的動做①與動做③都是把數據輸入給字節緩存,所以這兩個動做可視爲對字節緩存的寫操做。而動做②與動做④都是從字節緩存中取出數據,所以這兩個動做可視爲對字節緩存的讀操做。那麼反覆讀寫可能產生不一樣的處理需求,好比把當前位置挪回字節緩存的開頭,接下來是要寫入數據仍是讀出數據,爲此ByteBuffer又提供了下列四個方法:
clear:緩衝區數據寫入通道以後,若是還想把新數據寫入緩衝區,就要先調用clear方法清空它。
compact:只清除已經讀過的數據,剩餘的未讀數據會移到緩衝區開頭,新增的數據將加到未讀數據後面。
flip:把緩衝區從寫模式切換到讀模式。從緩衝區讀取數據以前,必須先調用flip方法。
rewind:讓緩衝區的指針回到開頭,以便從新再來一遍。
上面的四個方法在部分功能上互有異同點,爲了更好地梳理它們之間的區別,下面整理了一個表格,說明每一個方法在調用以後將會引發哪些參數的變化。
position limit mark
clear 0 容量大小 -1
compact 0 容量大小 -1
flip 0 上次的當前位置 -1
rewind 0 保持不變 -1緩存
就具體的代碼邏輯而言,通常在寫入字節緩存以前(上圖的動做①與動做③),須要先調用compact方法;在讀取字節緩存以前(上圖的動做②與動做④),須要先調用flip方法。固然若是是建立字節緩存後的第一次操做,就沒必要調用compact方法或者flip方法,由於在一開始字節緩存的當前位置都是指向0,無需再將當前位置挪回緩存開頭了。回頭看上一篇文章末尾經過文件通道讀取文件的代碼片斷:設計
int size = (int) channel.size(); // 獲取文件通道的大小(即文件長度) // 分配指定大小的字節緩存 ByteBuffer buffer = ByteBuffer.allocateDirect(size); channel.read(buffer); // 把文件通道中的數據讀到字節緩存 buffer.flip(); // 把緩衝區從寫模式切換到讀模式。從緩衝區讀取數據以前,必須先調用flip方法 byte[] bytes = new byte[size]; // 建立與文件大小相同長度的字節數組 buffer.get(bytes); // 把字節緩存中的數據取到字節數組
根據前面的文字介紹,可以很好地解釋以上代碼的方法調用次序。因爲通道對象的read方法是建立字節緩存以後的首個讀寫操做,所以無需先調用compact方法;而緩存對象的get方法不是首個讀寫操做,就必須在get以前先調用flip方法了。指針
更多Java技術文章參見《Java開發筆記(序)章節目錄》htm