如果將同步I/O方式下的數據傳輸比做數據傳輸的零星方式(這裡的零星是指在數據傳輸的過程中是以零星的字節方式進行的)那麼就可以將非阻塞I/O方式下的數據傳輸比做數據傳輸的集裝箱方式(在字節和低層數據傳輸之間多了一層緩沖區因此可以將緩沖區看做是裝載字節的集裝箱)大家可以想象如果我們要運送比較少的貨物用集裝箱好象有點不太合算而如果要運送上百噸的貨物用集裝箱來運送的成本會更低在數據傳輸過程中也是一樣如果數據量很小時使用同步I/O方式會更適合如果數據量很大時(一般以G為單位)使用非阻塞I/O方式的效率會更高因此從理論上說數據量越大使用非阻塞I/O方式的單位成本就會越低產生這種結果的原因和緩沖區的一些特性有著直接的關系在本節中將對緩沖區的一些主要特性進行講解使讀者可以充分理解緩沖區的概念並能通過緩沖區來提高程序的執行效率
創建緩沖區
Java提供了七個基本的緩沖區分別由七個類來管理它們都可以在javanio包中找到這七個類如下所示
ByteBuffer
ShortBuffer
IntBuffer
CharBuffer
FloatBuffer
DoubleBuffer
LongBuffer
這七個類中的方法類似只是它們的返回值或參數和相應的簡單類型相對應如ByteBuffer類的get方法返回了byte類型的數據而put方法需要一個byte類型的參數在CharBuffer類中的get和put方法返回和傳遞的數據類型就是char這七個類都沒有public構造方法因此它們不能通過new來創建相應的對象實例這些類都可以通過兩種方式來創建相應的對象實例
通過靜態方法allocate來創建緩沖區
這七類都有一個靜態的allocate方法通過這個方法可以創建有最大容量限制的緩沖區對象allocate的定義如下
ByteBuffer類中的allocate方法
public static ByteBuffer allocate(int capacity)
IntBuffer類中的allocate方法
public static IntBuffer allocate(int capacity)
其他五個緩沖區類中的allocate 方法定義和上面的定義類似只是返回值的類型是相應的緩沖區類
allocate方法有一個參數capacity用來指定緩沖區容量的最大值capacity的不能小於否則會拋出一個IllegalArgumentException異常使用allocate來創建緩沖區並不是一下子就分配給緩沖區capacity大小的空間而是根據緩沖區中存儲數據的情況來動態分配緩沖區的大小(實際上在低層Java采用了數據結構中的堆來管理緩沖區的大小)因此這個capacity可以是一個很大的值如*(M)allocate的使用方法如下
ByteBuffer byteBuffer = ByteBufferallocate();
IntBuffer intBuffer = IntBufferallocate();
在使用allocate創建緩沖區時應用注意capacity的含義隨著緩沖區的不同而不同如創建字節緩沖區時capacity指的是字節數而在創建整型(int)緩沖區時capacity指的是int型值的數目如果轉換成字數capacity的值應該乘如上面代碼中的intBuffer緩沖區最大可容納的字節數是* = 個
通過靜態方法wrap來創建緩沖區
使用allocate方法可以創建一個空的緩沖區而wrap方法可以利用已經存在的數據來創建緩沖區wrap方法可以將數組直接轉換成相應類型的緩沖區wrap方法有兩種重載形式它們的定義如下
ByteBuffer類中的wrap方法
public static ByteBuffer wrap(byte[] array)
public static ByteBuffer wrap(byte[] array int offset int length)
IntBuffer類中的wrap方法
public static IntBuffer wrap(byte[] array)
public static IntBuffer wrap(byte[] array int offset int length)
其他五個緩沖區類中的wrap 方法定義和上面的定義類似只是返回值的類型是相應的緩沖區類
在wrap方法中的array參數是要轉換的數組(如果是其他的緩沖區類數組的類型就是相應的簡單類型如IntBuffer類中的wrap方法的array就是int[]類型)offset是要轉換的子數組的偏移量也就是子數組在array中的開始索引length是要轉換的子數組的長度利用後兩個參數可以將array數組中的一部分轉換成緩沖區對象它們的使用方法如下
byte[] myByte = new byte[] { };
int[] myInt = new int[] { };
ByteBuffer byteBuffer = ByteBufferwrap(myByte);
IntBuffer intBuffer = IntBufferwrap(myInt );
可以通過緩沖區類的capacity方法來得到緩沖區的大小capacity方法的定義如下
public final int capacity()
如果使用allocate方法來創建緩沖區capacity方法的返回值就是capacity參數的值而使用wrap方法來創建緩沖區capacity方法的返回值是array數組的長度但要注意使用wrap來轉換array的字數組時capacity的長度仍然是原數組的長度如上面代碼中的intBuffer緩沖區的capacity值是而不是
除了可以將數組轉換成緩沖區外也可以通過緩沖區類的array方法將緩沖區轉換成相應類型的數組IntBuffer類的array方法的定義方法如下(其他緩沖區類的array的定義類似)
public final int[] array()
下面的代碼演示了如何使用array方法將緩沖區轉換成相應類型的數組
int[] myInt = new int[] { };
IntBuffer intBuffer = IntBufferwrap(myInt );
for (int v : intBufferarray())
Systemoutprint(v + );
在執行上面代碼後我們發現輸出的結果是 而不是 這說明在將子數組轉換成緩沖區的過程中實際上是將整個數組轉換成了緩沖區這就是用wrap包裝子數組後capacity的值仍然是原數組長度的真正原因在使用array方法時應注意在以下兩種緩沖區中不能使用array方法
只讀的緩沖區如果使用只讀緩沖區的array方法將會拋出一個ReadOnlyBufferException異常
使用allocateDirect方法創建的緩沖區
如果調用這種緩沖區中的array方法將會拋出一個UnsupportedOperationException異常
可以通過緩沖區類的hasArray方法來判斷這個緩沖區是否可以使用array方法如果返回true則說明這個緩沖區可以使用array方法否則使用array方法將會拋出上述的兩種異常之一
注意 使用array方法返回的數組並不是緩沖區數據的副本被返回的數組實際上就是緩沖區中的數據也就是說array方法只返回了緩沖區數據的引用當數組中的數據被修改後緩沖區中的數據也會被修改返之也是如此關於這方面內容將在下一節讀寫緩沖區中的數據中詳細講解
在上述的七個緩沖區類中ByteBuffer類和CharBuffer類各自還有另外一種方法來創建緩沖區對象
● ByteBuffer類
可以通過ByteBuffer類的allocateDirect方法來創建ByteBuffer對象allocateDirect方法的定義如下
public static ByteBuffer allocateDirect(int capacity)
使用allocateDirect方法可以一次性分配capacity大小的連續字節空間通過allocateDirect方法來創建具有連續空間的ByteBuffer對象雖然可以在一定程度上提高效率但這種方式並不是平台獨立的也就是說在一些操作系統平台上使用allocateDirect方法來創建ByteBuffer對象會使效率大幅度提高而在另一些操作系統平台上性能會表現得非常差而且allocateDirect方法需要較長的時間來分配內存空間在釋放空間時也較慢因此在使用allocateDirect方法時應謹慎
通過isDirect方法可以判斷緩沖區對象(其他的緩沖區類也有isDirect方法因為ByteBuffer對象可以轉換成其他的緩沖區對象這部分內容將在後面講解)是用哪種方式創建的如果isDirect方法返回true則這個緩沖區對象是用allocateDirect方法創建的否則就是用其他方法創建的緩沖區對象
● CharBuffer類
我們可以發現上述的七種緩沖區中並沒有字符串緩沖區而字符串在程序中卻是最常用的一種數據類型不過不要擔心雖然javanio包中並未提供字符串緩沖區但卻可以將字符串轉換成字符緩沖區(就是CharBuffer對象)在CharBuffer類中的wrap方法除了上述的兩種重載形式外又多了兩種重載形式它們的定義如下
public static CharBuffer wrap(CharSequence csq)
public static CharBuffer wrap(CharSequence csq int start int end)
其中csq參數表示要轉換的字符串但我們注意到csq的類型並不是String而是CharSequenceCharSequence類Java中四個可以表示字符串的類的父類這四個類是StringStringBufferStringBuilder和CharBuffer(大家要注意StringBuffer和本節講的緩沖區類一點關系都沒有這個類在javalang包中)也就是說CharBuffer類的wrap方法可以將這四個類的對象轉換成CharBuffer對象
另外兩個參數start和end分別是子字符串的開始索引和結束索引的下一個位置如將字符串中的 轉換成CharBuffer對象的語句如下
CharBuffer cb = CharBufferwrap( );
下面的代碼演示了如何使用wrap方法將不同形式的字符串轉換成CharBuffer對象
StringBuffer stringBuffer = new StringBuffer(通過StringBuffer創建CharBuffer對象);
StringBuilder stringBuilder = new StringBuilder(通過StringBuilder創建CharBuffer對象);
CharBuffer charBuffer = CharBufferwrap(通過String創建CharBuffer對象);
CharBuffer charBuffer = CharBufferwrap(stringBuffer);
CharBuffer charBuffer = CharBufferwrap(stringBuilder);
CharBuffer charBuffer = CharBufferwrap(charBuffer );
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26505.html