? ?Java NIO Buffer用來進行Channel之間的交互,如之前的文章所述,數(shù)據(jù)是從Channel讀入Buffer,再從Buffer寫入Channel中。
? ?一個Buffer實質(zhì)上就是一個可以寫入數(shù)據(jù)葛圃,然后從中讀出數(shù)據(jù)的內(nèi)存塊,這個內(nèi)存塊被包裝成一個NIO的Buffer對象,這個對象提供了一系列方法來使得處理內(nèi)存塊更加簡單装悲。
基礎(chǔ)的Buffer用法:
? ?使用一個Buffer來讀取和寫入數(shù)據(jù)通常遵循如下四個步驟:
1昏鹃、寫數(shù)據(jù)到Buffer
2、調(diào)用Buffer的flip()方法
3诀诊、從Buffer中讀出數(shù)據(jù)
4洞渤、調(diào)用Buffer的clear()或者compact()方法
? ?當(dāng)寫數(shù)據(jù)到Buffer中時,Buffer會追蹤已經(jīng)寫入的數(shù)據(jù)量属瓣。一旦你要讀取數(shù)據(jù)的時候载迄,你需要通過調(diào)用flip()
將Buffer的模式從寫入模式切換為讀取模式。在讀取模式下抡蛙,你可以讀取已寫入到Buffer中的所有數(shù)據(jù)护昧。
? ?一旦你已經(jīng)讀取所有的數(shù)據(jù),你需要清理Buffer來進行再次的寫入粗截。你可以通過兩種方式來實現(xiàn):調(diào)用clear()
方法或者調(diào)用compact()
方法惋耙。clear()
方法清理整個Buffer的的數(shù)據(jù),而compact()
方法僅清理已經(jīng)讀取過的數(shù)據(jù)熊昌,未讀取的數(shù)據(jù)將移入Buffer的開始位置绽榛,新寫入的數(shù)據(jù)將追加到未讀取的數(shù)據(jù)后邊。
? ?下面是一個Buffer的用例:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
//創(chuàng)建容量為48 bytes 的Buffer
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf); //讀取數(shù)據(jù)到Buffer中
while (bytesRead != -1) {
buf.flip(); //切換Buffer為讀取模式
while(buf.hasRemaining()){
System.out.print((char) buf.get()); // 每次讀取1byte的數(shù)據(jù)
}
buf.clear(); //將Buffer切換為寫入模式
bytesRead = inChannel.read(buf);
}
aFile.close();
Buffer的容量婿屹、位置和限制(Capacity灭美、Position和Limit)
? ?想要理解一個Buffer是如何工作的,你需要熟悉Buffer的三個屬性:
Capacity
Position
Limit
? ?Position和Limit的含義與Buffer處于讀取狀態(tài)還是寫入狀態(tài)有關(guān)昂利,而Capacity則是無論處于讀取狀態(tài)還是寫入狀態(tài)都是一樣的届腐。
? ?下面是寫入模式和讀取模式下Capacity、Position和Limit的說明:
Capacity
? ?作為一個內(nèi)存塊蜂奸,一個Buffer有一個確定的固定大小犁苏,也稱作capacity。你只能寫入這個大小的byte扩所、long傀顾、char等數(shù)據(jù)。一旦Buffer滿了碌奉,你就需要清理掉才能重新寫入數(shù)據(jù)。
Position
? ?當(dāng)你將數(shù)據(jù)寫入Buffer時寒砖,你是在一個確定的position寫入的赐劣,這個position的初始值是0,當(dāng)一個byte哩都、long等數(shù)據(jù)寫入這個Buffer之后魁兼,這個position也會隨著改變,指向即將要寫入數(shù)據(jù)的位置漠嵌,position的最大值是capacity - 1咐汞。
? ?當(dāng)你從Buffer中讀取數(shù)據(jù)時盖呼,你也要從一個給定的position中讀取,當(dāng)你將一個Buffer從寫入模式切換為讀取模式的時候化撕,這個position就會重置為0几晤,你讀取的數(shù)據(jù)也是從這個position的位置開始讀取,position將指向下一個要讀取的位置植阴。
Limit
? ?在寫入模式下蟹瘾,Buffer的limit就是你可以寫入的最大數(shù)據(jù)限制,在寫入模式下limit與Buffer的capacity相等掠手。
? ?當(dāng)切換為讀取模式的時候憾朴,limit就是你可以讀取的最大數(shù)據(jù)量。因此喷鸽,當(dāng)切換一個Buffer為讀取模式的時候众雷,limit就會被置為寫入模式下的position值。換句話說做祝,你可以讀取跟你寫入一樣多的數(shù)據(jù)量(你可以讀取所有你寫入的數(shù)據(jù))砾省。
Buffer的類型
? ?Java NIO有一些幾種類型:
ByteBuffer
MappedByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
? ?如你所見,這些Buffer類型代表著不同的數(shù)據(jù)類型剖淀。換句話說纯蛾,他們允許你使用這些類型來處理Buffer中的char、short纵隔、int翻诉、long、float和short類型數(shù)據(jù)捌刮。
? ?MappedByteBuffer有點特殊碰煌,我們將在專門的章節(jié)中來介紹。
分配Buffer
? ?為了獲取一個Buffer對象绅作,你首先要分配它芦圾。每個Buffer類有一個allocate()
方法來處理分配Buffer。
? ?下面是一個分配一個容量為48byte的ByteBuffer例子俄认。
ByteBuffer buf = ByteBuffer.allocate(48);
? ?下面是一個分配1024個字符空間的CharBuffer例子个少。
CharBuffer buf = CharBuffer.allocate(1024);
寫數(shù)據(jù)到Buffer中
? ?你可以按下面的兩種方法寫數(shù)據(jù)到一個Buffer中:
1、從一個Channel寫數(shù)據(jù)到一個Buffer中
2眯杏、通過Buffer的put()
方法將數(shù)據(jù)寫入到該Buffer中
? ?下面是一個將Channel數(shù)據(jù)寫入Buffer的例子:
int byteRead = inChannel.read(buf);
? ?下面是一個通過put()
方法寫數(shù)據(jù)到Channel的例子:
buf.put(127);
? ?通過put()方法寫數(shù)據(jù)的還有很多個多態(tài)方法夜焦,更多方法請參考Buffer的Doc文檔
flip()
? ?flip()
方法能夠?qū)⒁粋€Buffer從寫入狀態(tài)切換為讀取狀態(tài),調(diào)用flip()
方法也會limit重置為position的位置岂贩,將position重置為0茫经。
從Buffer中讀出數(shù)據(jù)
? ?有兩種方式來從一個Buffer中讀取數(shù)據(jù):
1、從Buffer中讀取數(shù)據(jù)到Channel
2、使用Buffer的get()
方法讀取該Buffer的數(shù)據(jù)
? ?下面是如何從Buffer中讀取數(shù)據(jù)到Channel的列子:
//從Buffer中讀取數(shù)據(jù)到Channel
int bytesWritten = inChannel.write(buf);
? ?下面是使用Buffer的get()方法讀取Buffer數(shù)據(jù)的例子:
byte aByte = buf.get()
rewind()
? ?Buffer的rewind()方法將position重置為0卸伞,這樣你就可以重新讀取Buffer中的所有數(shù)據(jù)了抹镊。limit保持不變,仍然標(biāo)識為可以從Buffer中可以讀出的元素個數(shù)
clear()和compact()
? ?一旦你讀完Buffer的數(shù)據(jù)后荤傲,你需要將Buffer再次置為準(zhǔn)備寫入的狀態(tài)垮耳,你可以調(diào)用clear()或者compact()方法來完成。
? ?如果你調(diào)用clear()方法弃酌,position將置為0氨菇,limit置為capacity,換句話中就是Buffer被清空了,真實的數(shù)據(jù)未被清空妓湘,只是告訴你數(shù)據(jù)位置的標(biāo)記被初始化了而已查蓉。
? ?如果Buffer中還有為讀取的數(shù)據(jù),如果你調(diào)用了clear()的話榜贴,未讀取的數(shù)據(jù)將會被遺忘豌研,也就是說不再有數(shù)據(jù)來指示哪些數(shù)據(jù)已經(jīng)讀取完,哪些數(shù)據(jù)還未讀取完唬党。
? ?如果Buffer中還有數(shù)據(jù)未讀取完鹃共,而你又想稍后再讀取,但是你要先寫入數(shù)據(jù)的話驶拱,你可以調(diào)用compact()而不是clear()霜浴。
? ?compact()拷貝所有未讀取的數(shù)據(jù)到Buffer的開始位置,然后設(shè)置position為未讀取數(shù)據(jù)之后蓝纲,limit與調(diào)用clear()一樣依然設(shè)置為capacity阴孟。這樣Buffer就切換為寫入模式,而未讀取的數(shù)據(jù)也不會被覆蓋税迷。
mark()和reset()
? ?你可以調(diào)用Buffer.mark()方法在Buffer中設(shè)置一個給定的position為標(biāo)記永丝,之后你就可以通過調(diào)用Buffer.reset()方法重新將position置為標(biāo)記的position了。
? ?下面就是一個例子:
buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing.
buffer.reset(); //set position back to mark.
equals()和compareTo()
? ?可以通過equals()和compareTo()來比較兩個Buffer
equals()
? ?滿足一下條件的話箭养,兩個Buffer就相等了:
1慕嚷、類型相同(byte、char毕泌、int等)
2喝检、Buffer中剩余相同的數(shù)量的bytes、chars撼泛、int等
3蛇耀、所有剩余的bytes、chars坎弯、int等相同
? ?如你所見,equals僅比較Buffer的部分數(shù)據(jù),而不是它內(nèi)部的每個元素抠忘。實際上撩炊,它只是比較Buffer中的剩余元素。
compareTo()
? ?compareTo()方法比較兩個Buffer中的剩余元素崎脉,如果滿足以下條件拧咳,則認為一個Buffer比另一個"小":
1、Buffer中的第一個元素與另一個Buffer中對應(yīng)的第一個元素比較囚灼,小于另一個Buffer的第一個元素
2骆膝、所有元素都相等,但是第一個Buffer的元素個數(shù)小于另一個Buffer的元素個數(shù)