概述
在這節中,我們將關註NIO的Buffer中兩個重要的組件:狀態變量和訪問方法。
狀態變量對於前面提到的“內部計數系統”而言相當重要,每次進行完讀寫之後,Buffer的狀態都隨之改變。通過記錄和跟蹤這些改變,Buffer才可以把Buffer內部的資源管理好。
當你從Channel中讀數據時,數據首先放到瞭Buffer中。在某些情況下,你可以直接把這個Buffer寫入另一個Channel中,但是通常情況下,你可能想看看數據內容,這個想法可以通過方法get()實現。相似的,當你想要把原始數據放進Buffer中,你可以使用put()方法。
在這節中,我們將學習NIO中的狀態變量和訪問方法。每個組件都會涉及到,並且有機會查看具體的使用。然而NIO的內部計數系統起初看起來可能有些復雜,你很快會看到內部計數系統實際上為你做瞭哪些事情。
狀態變量
總共有三個值可以被用來表示在給定的任何時刻Buffer的狀態,他們分別是:
position
limit
capacity
這三個變量跟蹤瞭Buffer的狀態和Buffer所包含的數據。
接下來我們將逐一檢查每個細節,並且也看看為什麼這樣的設計適合典型的讀/寫(輸入/輸出)處理。比如僅僅這個例子,我們假設從一個Channel拷貝數據到另一個Channel。
Position
回憶一下,Buffer實際上也就是個array。當你從Channel中讀數據時,你把從Channel中讀出來的數據放進底層array,position變量用來跟蹤截止目前為止已經寫瞭多少數據。更精確的講,它指示如果下次寫Buffer時數據應該進入array的哪個位置。因此如果已經從Channel中讀出瞭3個字節,Buffer的position會被置為3,指向array中第四個位置。
相似的,如果正在向Channel中寫入數據,你需要從Buffer中獲取要寫的數據,此時position持續的跟蹤你已經從Buffer中讀取瞭多少數據。更精確的說,position指示瞭下一次從Buffer中讀取數據時將讀入array的哪個元素。因此如果你已經向Channel中寫瞭5個byte,Buffer的position被置為5,指向array中第六個元素。
Limit
在從Buffer中向Channel中寫數據時,limit變量指示瞭還剩多少數據可以讀取,在從Channel中讀取數據到Buffer中時,limit變量指示瞭還剩多少空間可供存放數據。
position正常情況下小於或者等於limit。
Capacity
Buffer的Capacity指示Buffer最多能夠存儲的數據。實際上,它指示瞭底層array的容量,或者至少是底層array允許使用的空間數量。
Limit永遠不會大於capacity。
以實例來觀察這三個變量
我們從一個新建的Buffer開始。由於是例子的緣故,我們假設Buffer有一個8字節大小的Capacity。此時Buffer的狀態如下所示:
回憶之前所講的,limit不會大於capacity,在這個例子中,limit和capacity都會被設為8。我們通過在array尾部用箭頭指示的方式表示。
此時position設置為0。如果我們從Channel讀瞭一些數據進入Buffer,下一個字節將會被存入位置為0的地方。如果我們從Buffer中寫數據進入Channel,Buffer中下一個被讀的字節將從位置0取得。position的設置如下圖所示:
因為capacity一旦設置好就不會改變瞭,之後的討論我們將暫時忽略capacity。
第一次讀操作
現在我們準備好瞭在我們新建的Buffer上開始讀/寫操作瞭。我們開始從Channel中讀一些數據進入Buffer,第一次讀3個字節。讀之前position為0,讀完後position從0增長到3,如下所示:
第二次讀操作
第二次讀操作,我們將再讀2個字節從Channel到Buffer中,這兩個字節存儲的位置從之前的position即3開始,讀完後position增加瞭2變為5。如下圖:
Flip操作
到目前位置,我們結束瞭從Channel中讀的操作,現在開始將數據寫入輸出Channel中。在做寫操作之前,我們必須調用一次flip()方法,這個方法做瞭兩件重要的事情:
1. 將limit設置到當前的position處。
2. 設置position為0。
上幅圖展示瞭在執行flip之前的Buffer,下面這幅圖展示瞭執行瞭flip() 之後的Buffer:
現在我們已經準備好瞭把數據從Buffer中寫到Channel中。Position已經設置為0,意思是說我們將獲得的下一個字節為位置為0的字節,limit已經設置到瞭之前的position處,意思是此時Buffer中包含有之前讀入Buffer中的所有字節。
第一次寫操作
在我們第一次的寫操作中,我們從Buffer中取出4個字節,然後寫入output channel中。這個操作使得position從0增加到4,而limit沒有變化,如下所示:
第二次寫操作
目前為止,隻剩一個字節可寫瞭。當我們調用flip()時,limit被設置成5,並且position不能超越limit。因此最後的寫操作從Buffer取出一個字節並寫入output Channel中。這次寫操作會將position增加到5,而limit不變。如下所示:
Clear操作
在我們的最後一步是調用Clear方法。這個方法會重置Buffer以準備接收新數據。Clear做瞭2件重要的事情:
1. 設置limit為0以匹配capacity。
2. 設置position為0。
下面的圖展示瞭當調用完flip()之後的Buffer的狀態:
一個能工作的Buffer
下面的代碼總結瞭使用Buffer從一個Channel拷貝數據到另一個Channel
Java代碼
while(trie) {
buffer.clear();
int r = fcin.read( buffer );
if (r==-1) {
break;
}
buffer.flip();
fcout.write( buffer );
}
read()和write()方法極大的簡化瞭程序,由於Buffer處理瞭所有的細節。clear()和flip()方法用來切換Buffer的讀和寫。
作者“guibin”