Java NIO教程 Buffer


緩沖區本質上是一塊可以寫入數據,然后可以從中讀取數據的內存,這塊內存中有很多可以存儲byte(或int、char等)的小單元。這塊內存被包裝成NIO Buffer對象,並提供了一組方法,用來方便的訪問該塊內存。

為了理解Buffer的工作原理,需要熟悉它的三個屬性:

  • capacity
  • position
  • limit

簡單的解釋這三個屬性的含義可以概括為:capacity代表這塊Buffer的容量,position代表下一次讀(或寫)的位置,limit代表本次讀(或寫)的極限位置。這么簡單說一下你當然是聽不懂的啦(這樣一下你就聽懂了,豈不是顯得我很沒有存在感)所以下面開始詳細的講解。
Buffer圖示
capacity

作為一個內存塊,Buffer有一個固定的大小值(這個大小是剛開始申請的),叫作“capacity”.你只能往里寫capacity個byte、int,char等類型。

position

當你要寫數據到Buffer中時,position表示當前可寫的位置。初始的position值為0(也可以用過方法進行改變)。當一個byte、int等數據寫到Buffer后, position會向前移動到下一個可插入數據的Buffer單元。position最大可為capacity – 1.

當讀取數據時,也是從某個特定位置讀。當從Buffer的position處讀取數據完成時,position向前移動到下一個可讀的位置。

limit

在寫數據時,Buffer的limit表示你最多能往Buffer里寫多少數據,position移動到limit寫操作停止。初始limit的值等於Buffer的capacity。當讀取數據時, limit表示你最多能讀到多少數據,position移動到limit讀操作停止。

無論在讀數據時還是在寫數據時,只要position超過了limit就會拋出異常。

控制position和limit的值

capacity的值是根據申請Buffer的大小和種類確定的,所以不能改變。而position和limit就可以根據我的需要而改變了,首先介紹一下如何查看這三個屬性的值:buffer.limit()buffer.position()buffer.capacity()這三個方法直觀、方便,我們就不再羅嗦。

接下來我們要着重看一下buffer.flip()這個方法一般用在寫到讀切換的時候。這個方法的能力就是將limit設為position的值,再是將position設為0。

這么做的用處是什么呢?你想想,在寫數據的時候從Buffer的開始處—0位置position位置之間已經寫滿了數據,如果這時候我們想要從頭開始讀數據的話,就要將position指向0,以便可以讀取0位置的數據,然后逐個向下讀取;但要讀到什么位置為止呢?如果整個Buffer都讀完的話,剛才所寫的最后一個單元以后的單元,都是空,讀取它們沒有意義。所以讀取到剛才所寫的最后一個單元,是明智之舉。而在將position置為0之前,position值就是剛才所寫的最后一個單元的位置。所以在寫到讀切換的時候,將limit設為position的值,再是將position設為0。

這個明白了以后一切都順了。buffer.clear()是清空Buffer的方法,但它沒有真正的清除,只是將position置為0,將limit置為capacity;這樣一來,你的寫操作就可以將原來的數據覆蓋了。就是這么簡單。buffer.rewind()是將position置為0,這樣一來就可以將buffer再重新讀一遍,當然你還可通過它干很多事。

其實還有很多有用、有趣的方法,看看api文檔吧。

基礎實例 和 btye與其他類型的轉換

說了這么多理論,再不上代碼就有人得罵街了,來個最基礎的申請buffer和基本的讀寫吧

ByteBuffer bb = ByteBuffer.allocate(48);
/*向ByteBuffer中put數據的時候,一下四種形式都可以
 * put(byte b)	
 * put(byte[] src) 
 * put(byte[] src, int offset, int length)
 * put(ByteBuffer src)
 * 四種形式都會移動position指針
 */
bb.put(new byte[]{1,2,4,2,-13});
bb.flip();
//hasRemaining()的作用是看看position到沒到limit位置
while(bb.hasRemaining()) {
	System.out.println(bb.get());
}

還得來一段理論,再聽我扯一會。Buffer共有類型有以下幾種

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer
  • MappedByteBuffer(這哥們兒有點特殊,以后單獨再講)

Buffer在nio中的主要作用就是與channel交互。但這幾種類型中能與channel交互的只有ByteBuffer(坑爹的吧!)所以在用其他類型Buffer的時候,一般都是先將ByteBuffer轉化為想用的類型,用的是byteBuffer.asCharBuffer()byteBuffer.asIntBuffer()等方法進行轉換。這種方式用術語來講,叫做“產生其他種類Buffer的視圖”;意思就是底層是ByteBuffer,但看起來是其它種類的Buffer,可以用相應的方法,但是視圖發生了讀寫,底層的ByteBuffer也會發生變化。

下面這段是將ByteBuffer轉化為CharBuffer視圖的例子

ByteBuffer bb = ByteBuffer.allocate(1024);
//將ByteBuffer轉化為CharBuffer視圖后,再調用put,ByteBuffer中的position指針不會移動
bb.asCharBuffer().put("Hello World");
//為了能正確的輸出,這里改變了limit指針的位置,使之變到了字符數組的末尾
bb.limit("Hello World".length()*Character.BYTES);//字符數組長度*每個字符占的字節數
while(bb.hasRemaining()) {
	System.out.print(bb.getChar());
}
/*也可用如下的方法輸出
 * while((c=bb.getChar())!=0) {
 *    System.out.print(c);
 * }
 */

這段例子告訴我們,視圖發生了讀寫,底層的ByteBuffer是有感知的,以及感知如何展現出來。但真正用的時候,沒這么蠻煩。因為學了后面的channel,就知道了,channel直接就把整個ByteBuffer都拿走了,就不用這樣一個個的輸出了。而且一個個輸出的話也可以直接利用視圖層,向下看

ByteBuffer bb = ByteBuffer.allocate(1024);
IntBuffer ib = bb.asIntBuffer();
ib.put(new int[]{1,42,12,-12});
/*將ByteBuffer轉化為IntBuffer視圖后,再調用put,ByteBuffer中的position指針不會移動
 * 但是所生成的IntBuffer中的position會按正常方式移動
 * 而且整個IntBuffer的capacity會按照byte 和 int 之間的所占字節大小比例而改變*/
System.out.println("ByteBuffer.position = "+bb.position());
System.out.println("ByteBuffer.limit = "+bb.limit());
System.out.println("ByteBuffer.capacity = "+bb.capacity());
System.out.println("IntBuffer.position = "+ib.position());
System.out.println("IntBuffer.limit = "+ib.limit());
System.out.println("IntBuffer.capacity = "+ib.capacity());
ib.flip();
while(ib.hasRemaining()) {
	System.out.println(ib.get());
}

會了這些Buffer的知識就差不多了,就到這里了。多打打例子代碼、多體會體會,就可以洗洗睡了,拜拜

還是那句話,有問題及時告訴我


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM