Java NIO3:缓冲区Buffer
???? 在上一篇中,我們介紹了NIO中的兩個核心對象:緩沖區和通道,在談到緩沖區時,我們說緩沖區對象本質上是一個數組,但它其實是一個特殊的數組,緩沖區對象內置了一些機制,能夠跟蹤和記錄緩沖區的狀態變化情況,如果我們使用get()方法從緩沖區獲取數據或者使用put()方法把數據寫入緩沖區,都會引起緩沖區狀態的變化。
在緩沖區中,最重要的屬性有下面三個,它們一起合作完成對緩沖區內部狀態的變化跟蹤:
position:指定了下一個將要被寫入或者讀取的元素索引,它的值由get()/put()方法自動更新,在新創建一個Buffer對象時,position被初始化為0。
limit:指定還有多少數據需要取出(在從緩沖區寫入通道時),或者還有多少空間可以放入數據(在從通道讀入緩沖區時)。
capacity:指定了可以存儲在緩沖區中的最大數據容量,實際上,它指定了底層數組的大小,或者至少是指定了準許我們使用的底層數組的容量。
???? 以上四個屬性值之間有一些相對大小的關系:0 <= position <= limit <= capacity。如果我們創建一個新的容量大小為10的ByteBuffer對象,在初始化的時候,position設置為0,limit和 capacity被設置為10,在以后使用ByteBuffer對象過程中,capacity的值不會再發生變化,而其它兩個個將會隨著使用而變化。四個屬性值分別如圖所示:
???? 現在我們可以從通道中讀取一些數據到緩沖區中,注意從通道讀取數據,相當于往緩沖區中寫入數據。如果讀取4個自己的數據,則此時position的值為4,即下一個將要被寫入的字節索引為4,而limit仍然是10,如下圖所示:?
下一步把讀取的數據寫入到輸出通道中,相當于從緩沖區中讀取數據,在此之前,必須調用flip()方法,該方法將會完成兩件事情:
1. 把limit設置為當前的position值
2. 把position設置為0
由于position被設置為0,所以可以保證在下一步輸出時讀取到的是緩沖區中的第一個字節,而limit被設置為當前的position,可以保證讀取的數據正好是之前寫入到緩沖區中的數據,如下圖所示:
現在調用get()方法從緩沖區中讀取數據寫入到輸出通道,這會導致position的增加而limit保持不變,但position不會超過limit的值,所以在讀取我們之前寫入到緩沖區中的4個自己之后,position和limit的值都為4,如下圖所示:
在從緩沖區中讀取數據完畢后,limit的值仍然保持在我們調用flip()方法時的值,調用clear()方法能夠把所有的狀態變化設置為初始化時的值,如下圖所示:
下面用一段代碼來驗證這個過程,如下所示:?
package com.demo.nio;import java.io.FileInputStream; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.channels.FileChannel;public class TestBuffer {public static void main(String[] args) throws Exception{FileInputStream fin = new FileInputStream("c:\\test.txt");FileChannel fc = fin.getChannel();ByteBuffer buffer = ByteBuffer.allocate(10);output("初始化",buffer);fc.read(buffer);output("調用read()", buffer);buffer.flip();output("調用flip()", buffer);while (buffer.remaining() > 0) {byte b = buffer.get();// System.out.print(((char)b)); }output("調用get()", buffer);buffer.clear();output("調用clear()", buffer);fin.close();}public static void output(String step, Buffer buffer) {System.out.println(step + " : ");System.out.print("capacity: " + buffer.capacity() + ", ");System.out.print("position: " + buffer.position() + ", ");System.out.println("limit: " + buffer.limit());System.out.println();}}完成的輸出結果為:
緩沖區的分配
????? 在前面的幾個例子中,我們已經看過了,在創建一個緩沖區對象時,會調用靜態方法allocate()來指定緩沖區的容量,其實調用 allocate()相當于創建了一個指定大小的數組,并把它包裝為緩沖區對象。或者我們也可以直接將一個現有的數組,包裝為緩沖區對象,如下示例代碼所示:
public class BufferWrap {public void myMethod(){// 分配指定大小的緩沖區ByteBuffer buffer1 = ByteBuffer.allocate(10);// 包裝一個現有的數組byte array[] = new byte[10];ByteBuffer buffer2 = ByteBuffer.wrap( array );} }緩沖區分片:
???? 在NIO中,除了可以分配或者包裝一個緩沖區對象外,還可以根據現有的緩沖區對象來創建一個子緩沖區,即在現有緩沖區上切出一片來作為一個新的緩沖區,但現有的緩沖區與創建的子緩沖區在底層數組層面上是數據共享的,也就是說,子緩沖區相當于是現有緩沖區的一個視圖窗口。調用slice()方法可以創建一個子緩沖區,讓我們通過例子來看一下:
@Test public void testSliceBuffer(){ByteBuffer buffer = ByteBuffer.allocate(10);// 緩沖區中的數據0-9for (int i=0; i<buffer.capacity(); i++) {buffer.put( (byte)i );}// 創建子緩沖區buffer.position(3);buffer.limit(7);ByteBuffer slice = buffer.slice();// 改變子緩沖區的內容for (int i=0; i<slice.capacity(); i++) {byte b = slice.get( i );b *= 10;slice.put( i, b );}buffer.position( 0 );buffer.limit( buffer.capacity() );while (buffer.remaining()>0) {System.out.println( buffer.get() );}System.out.print("\n"); }在該示例中,分配了一個容量大小為10的緩沖區,并在其中放入了數據0-9,而在該緩沖區基礎之上又創建了一個子緩沖區,并改變子緩沖區中的內容,從最后輸出的結果來看,只有子緩沖區“可見的”那部分數據發生了變化,并且說明子緩沖區與原緩沖區是數據共享的,輸出結果如下所示:
只讀緩沖區
????? 只讀緩沖區非常簡單,可以讀取它們,但是不能向它們寫入數據。可以通過調用緩沖區的asReadOnlyBuffer()方法,將任何常規緩沖區轉 換為只讀緩沖區,這個方法返回一個與原緩沖區完全相同的緩沖區,并與原緩沖區共享數據,只不過它是只讀的。如果原緩沖區的內容發生了變化,只讀緩沖區的內容也隨之發生變化:
@Test public void testReadOnlyBuffer(){ByteBuffer buffer = ByteBuffer.allocate( 10 );// 緩沖區中的數據0-9for (int i=0; i<buffer.capacity(); ++i) {buffer.put( (byte)i );}// 創建只讀緩沖區ByteBuffer readonly = buffer.asReadOnlyBuffer();// 改變原緩沖區的內容for (int i=0; i<buffer.capacity(); ++i) {byte b = buffer.get( i );b *= 10;buffer.put( i, b );}readonly.position(0);readonly.limit(buffer.capacity());// 只讀緩沖區的內容也隨之改變while (readonly.remaining()>0) {System.out.println( readonly.get());}}運行結果如下所示:
如果嘗試修改只讀緩沖區的內容,則會報ReadOnlyBufferException異常。只讀緩沖區對于保護數據很有用。在將緩沖區傳遞給某個對象的方法時,無法知道這個方法是否會修改緩沖區中的數據。創建一個只讀的緩沖區可以保證該緩沖區不會被修改。只可以把常規緩沖區轉換為只讀緩沖區,而不能將只讀的緩沖區轉換為可寫的緩沖區。
總結
以上是生活随笔為你收集整理的Java NIO3:缓冲区Buffer的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 121 项目 023 笔记向 反射
- 下一篇: 允许MySQL 帐号远程登录