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