- NIO與Socket編程技術(shù)指南
- 高洪巖
- 3552字
- 2019-01-05 05:42:04
1.4 ByteBuffer類的使用
ByteBuffer類是Buffer類的子類,可以在緩沖區(qū)中以字節(jié)為單位對數(shù)據(jù)進行存取,而且它也是比較常用和重要的緩沖區(qū)類。在使用NIO技術(shù)時,有很大的概率使用ByteBuffer類來進行數(shù)據(jù)的處理。
在前面的示例中已經(jīng)使用過ByteBuffer類,該類的API列表如圖1-19所示。

圖1-19 ByteBuffer類的API列表
ByteBuffer類提供了6類操作。
1)以絕對位置和相對位置讀寫單個字節(jié)的get()和put()方法。
2)使用相對批量get(byte[] dst)方法可以將緩沖區(qū)中的連續(xù)字節(jié)傳輸?shù)絙yte[] dst目標數(shù)組中。
3)使用相對批量put(byte[] src)方法可以將byte[]數(shù)組或其他字節(jié)緩沖區(qū)中的連續(xù)字節(jié)存儲到此緩沖區(qū)中。
4)使用絕對和相對getType和putType方法可以按照字節(jié)順序在字節(jié)序列中讀寫其他基本數(shù)據(jù)類型的值,方法getType和putType可以進行數(shù)據(jù)類型的自動轉(zhuǎn)換。
5)提供了創(chuàng)建視圖緩沖區(qū)的方法,這些方法允許將字節(jié)緩沖區(qū)視為包含其他基本類型值的緩沖區(qū),這些方法有asCharBuffer()、asDoubleBuffer()、asFloatBuffer()、asIntBuffer()、asLongBuffer()和asShortBuffer()。
6)提供了對字節(jié)緩沖區(qū)進行壓縮(compacting)、復(fù)制(duplicating)和截取(slicing)的方法。
字節(jié)緩沖區(qū)可以通過allocation()方法創(chuàng)建,此方法為緩沖區(qū)的內(nèi)容分配空間,或者通過wrapping方法將現(xiàn)有的byte[]數(shù)組包裝到緩沖區(qū)中來創(chuàng)建。
本節(jié)將把ByteBuffer類所有API的功能進行展示,以促進對該類的學(xué)習(xí)與掌握。
1.4.1 創(chuàng)建堆緩沖區(qū)與直接緩沖區(qū)
字節(jié)緩沖區(qū)分為直接字節(jié)緩沖區(qū)與非直接字節(jié)緩沖區(qū)。
如果字節(jié)緩沖區(qū)為直接字節(jié)緩沖區(qū),則JVM會盡量在直接字節(jié)緩沖區(qū)上執(zhí)行本機I/O操作,也就是直接對內(nèi)核空間進行訪問,以提高運行效率。提高運行效率的原理就是在每次調(diào)用基于操作系統(tǒng)的I/O操作之前或之后,JVM都會盡量避免將緩沖區(qū)的內(nèi)容復(fù)制到中間緩沖區(qū)中,或者從中間緩沖區(qū)中復(fù)制內(nèi)容,這樣就節(jié)省了一個步驟。
工廠方法allocateDirect()可以創(chuàng)建直接字節(jié)緩沖區(qū),通過工廠方法allocateDirect()返回的緩沖區(qū)進行內(nèi)存的分配和釋放所需的時間成本通常要高于非直接緩沖區(qū)。直接緩沖區(qū)操作的數(shù)據(jù)不在JVM堆中,而是在內(nèi)核空間中,根據(jù)這個結(jié)構(gòu)可以分析出,直接緩沖區(qū)善于保存那些易受操作系統(tǒng)本機I/O操作影響的大量、長時間保存的數(shù)據(jù)。
allocateDirect(int capacity)方法的作用:分配新的直接字節(jié)緩沖區(qū)。新緩沖區(qū)的位置將為零,其界限將為其容量,其標記是不確定的。無論它是否具有底層實現(xiàn)數(shù)組,其標記都是不確定的。
allocate(int capacity)方法的作用:分配一個新的非直接字節(jié)緩沖區(qū)。新緩沖區(qū)的位置為零,其界限將為其容量,其標記是不確定的。它將具有一個底層實現(xiàn)數(shù)組,且其數(shù)組偏移量將為零。
在JDK中,可以查看一下allocate()方法的源代碼,從中會發(fā)現(xiàn)其會創(chuàng)建一個新的數(shù)組,而wrap()方法是使用傳入的數(shù)組作為存儲空間,說明對wrap()關(guān)聯(lián)的數(shù)組進行操作會影響到緩沖區(qū)中的數(shù)據(jù),而操作緩沖區(qū)中的數(shù)據(jù)也會影響到與wrap()關(guān)聯(lián)的數(shù)組中的數(shù)據(jù),原理其實就是引用同一個數(shù)組對象。
示例代碼如下:
public class Test1 { public static void main(String[] args) { ByteBuffer bytebuffer1 = ByteBuffer.allocateDirect(100); ByteBuffer bytebuffer2 = ByteBuffer.allocate(200); System.out.println("bytebuffer1 position=" + bytebuffer1.position() + " limit=" + bytebuffer1.limit()); System.out.println("bytebuffer2 position=" + bytebuffer2.position() + " limit=" + bytebuffer2.limit()); System.out.println("bytebuffer1=" + bytebuffer1 + " isDirect=" + bytebuffer1. isDirect()); System.out.println("bytebuffer2=" + bytebuffer2 + " isDirect=" + bytebuffer2. isDirect()); } }
程序運行結(jié)果如下:
bytebuffer1 position=0 limit=100 bytebuffer2 position=0 limit=200 bytebuffer1=java.nio.DirectByteBuffer[pos=0 lim=100 cap=100] isDirect=true bytebuffer2=java.nio.HeapByteBuffer[pos=0 lim=200 cap=200] isDirect=false
使用allocateDirect()方法創(chuàng)建出來的緩沖區(qū)類型為DirectByteBuffer,使用allocate()方法創(chuàng)建出來的緩沖區(qū)類型為HeapByteBuffer。
使用allocateDirect()方法創(chuàng)建ByteBuffer緩沖區(qū)時,capacity指的是字節(jié)的個數(shù),而創(chuàng)建IntBuffer緩沖區(qū)時,capacity指的是int值的數(shù)目,如果要轉(zhuǎn)換成字節(jié),則capacity的值要乘以4,來算出占用的總字節(jié)數(shù)。
使用allocateDirect()方法創(chuàng)建的直接緩沖區(qū)如何釋放內(nèi)存呢?有兩種辦法,一種是手動釋放空間,另一種就是交給JVM進行處理。先來看第一種:手動釋放空間,示例代碼如下:
public class Test1 { public static void main(String[] args) throws NoSuchMethodException, Security Exception, IllegalAccessException, IllegalArgumentException, InvocationTargetException, Interrupted Exception { System.out.println("A"); ByteBuffer buffer = ByteBuffer.allocateDirect(Integer.MAX_VALUE); System.out.println("B"); byte[] byteArray = new byte[] { 1 }; System.out.println(Integer.MAX_VALUE); for (int i = 0; i < Integer.MAX_VALUE; i++) { buffer.put(byteArray); } System.out.println("put end! "); Thread.sleep(1000); Method cleanerMethod = buffer.getClass().getMethod("cleaner"); cleanerMethod.setAccessible(true); Object returnValue = cleanerMethod.invoke(buffer); Method cleanMethod = returnValue.getClass().getMethod("clean"); cleanMethod.setAccessible(true); cleanMethod.invoke(returnValue); // 此程序運行的效果就是1秒鐘之后立即回收內(nèi)存 // 也就是回收“直接緩沖區(qū)”所占用的內(nèi)存 } }
程序運行后,可在“Windows任務(wù)管理器”的“性能”標簽頁的“物理內(nèi)存”選項的“可用”節(jié)點中查看內(nèi)存的使用情況。
另一種就是由JVM進行自動化的處理,示例代碼如下:
public class Test2 { public static void main(String[] args) throws NoSuchMethodException, Security Exception, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InterruptedException { System.out.println("A"); ByteBuffer buffer = ByteBuffer.allocateDirect(Integer.MAX_VALUE); System.out.println("B"); byte[] byteArray = new byte[] { 1 }; System.out.println(Integer.MAX_VALUE); for (int i = 0; i < Integer.MAX_VALUE; i++) { buffer.put(byteArray); } System.out.println("put end! "); // 此程序多次運行后,一直在耗費內(nèi)存, // 進程結(jié)束后,也不會馬上回收內(nèi)存, // 而是會在某個時機觸發(fā)GC垃圾回收器進行內(nèi)存的回收 } }
在Windows 7系統(tǒng)中出現(xiàn)的現(xiàn)象就是進程結(jié)束后,Windows 7并不立即回收內(nèi)存,而是在某一個時機回收。
此*.java類可以運行多次,產(chǎn)生多個進程,然后再查看內(nèi)存使用情況會更加直觀。
1.4.2 直接緩沖區(qū)與非直接緩沖區(qū)的運行效率比較
直接緩沖區(qū)會直接作用于本地操作系統(tǒng)的I/O,處理數(shù)據(jù)的效率相比非直接緩沖區(qū)會快一些。
可以創(chuàng)建兩者性能比較用的測試程序,如使用直接緩沖區(qū)來看看用時是多少,源代碼如下:
public class Test1_2 { public static void main(String[] args) { long beginTime = System.currentTimeMillis(); ByteBuffer buffer = ByteBuffer.allocateDirect(1900000000); for (int i = 0; i < 1900000000; i++) { buffer.put((byte) 123); } long endTime = System.currentTimeMillis(); System.out.println(endTime - beginTime); } }
程序運行結(jié)果如下:
1840
使用非直接緩沖區(qū)的測試代碼如下:
public class Test1_3 { public static void main(String[] args) { long beginTime = System.currentTimeMillis(); ByteBuffer buffer = ByteBuffer.allocate(1900000000); for (int i = 0; i < 1900000000; i++) { buffer.put((byte) 123); } long endTime = System.currentTimeMillis(); System.out.println(endTime - beginTime); } }
程序運行結(jié)果如下:
2309
從運行結(jié)果來看,直接緩沖區(qū)比非直接緩沖區(qū)在運行效率上要高一些,是什么原因造成這樣的結(jié)果呢?直接緩沖區(qū)是使用DirectByteBuffer類進行實現(xiàn)的,而非直接緩沖區(qū)是使用HeapByteBuffer類進行實現(xiàn)的。直接緩沖區(qū)的實現(xiàn)類DirectByteBuffer的put(byte)方法的源代碼如下:
public ByteBuffer put(byte x) {
unsafe.putByte(ix(nextPutIndex()), ((x)));
return this;
}
直接緩沖區(qū)(DirectByteBuffer)在內(nèi)部使用sun.misc.Unsafe類進行值的處理。Unsafe類的作用是JVM與操作系統(tǒng)進行直接通信,提高程序運行的效率,但正如其類的名稱Unsafe一樣,該類在使用上并不是安全的,如果程序員使用不當,那么極有可能出現(xiàn)處理數(shù)據(jù)上的錯誤,因此,該類并沒有公開化(public),僅由JDK內(nèi)部使用。
而非直接緩沖區(qū)的實現(xiàn)類HeapByteBuffer的put(byte)方法的源代碼如下:
public ByteBuffer put(byte x) { hb[ix(nextPutIndex())] = x; return this; }
非直接緩沖區(qū)(HeapByteBuffer)在內(nèi)部直接對byte[] hb字節(jié)數(shù)組進行操作,而且還是在JVM的堆中進行數(shù)據(jù)處理,因此運行效率相對慢一些。
1.4.3 包裝wrap數(shù)據(jù)的處理
wrap(byte[] array)方法的作用:將byte數(shù)組包裝到緩沖區(qū)中。新的緩沖區(qū)將由給定的byte數(shù)組支持,也就是說,緩沖區(qū)修改將導(dǎo)致數(shù)組修改,反之亦然。新緩沖區(qū)的capacity和limit將為array.length,其位置position將為0,其標記mark是不確定的。其底層實現(xiàn)數(shù)組將為給定數(shù)組,并且其arrayOffset將為0。
wrap(byte[] array, int offset, int length)方法的作用:將byte數(shù)組包裝到緩沖區(qū)中。新的緩沖區(qū)將由給定的byte數(shù)組支持,也就是說,緩沖區(qū)修改將導(dǎo)致數(shù)組修改,反之亦然。新緩沖區(qū)的capacity將為array.length,其position將為offset,其limit將為offset + length,其標記是不確定的。其底層實現(xiàn)數(shù)組將為給定數(shù)組,并且其arrayOffset將為0。
相關(guān)參數(shù)的解釋如下。
1)array:緩沖區(qū)中關(guān)聯(lián)的字節(jié)數(shù)組。
2)offset:設(shè)置位置(position)值,該值必須為非負且不大于array.length。
3)length:將新緩沖區(qū)的界限設(shè)置為offset + length,該值必須為非負且不大于array. length-offset。
注意:wrap(byte[] array, int offset, int length)方法并不具有subString()方法截取的作用,它的參數(shù)offset只是設(shè)置緩沖區(qū)的position值,而length確定limit值。
示例代碼如下:
public class Test2 { public static void main(String[] args) { byte[] byteArray = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArray); ByteBuffer bytebuffer2 = ByteBuffer.wrap(byteArray, 2, 4); System.out.println("bytebuffer1 capacity=" + bytebuffer1.capacity() + " limit=" + bytebuffer1.limit() + " position=" + bytebuffer1.position()); System.out.println(); System.out.println("bytebuffer2 capacity=" + bytebuffer2.capacity() + " limit=" + bytebuffer2.limit() + " position=" + bytebuffer2.position()); } }
程序運行結(jié)果如下:
bytebuffer1 capacity=8 limit=8 position=0 bytebuffer2 capacity=8 limit=6 position=2
Buffer類的每個子類都定義了兩種get(讀)和put(寫)操作,分別對應(yīng)相對位置操作和絕對位置操作。
相對位置操作是指在讀取或?qū)懭胍粋€或多個元素時,它從“當前位置開始”,然后將位置增加所傳輸?shù)脑財?shù)。如果請求的傳輸超出限制,則相對get操作將拋出BufferUnder flowException異常,相對put操作將拋出BufferOverflowException異常,也就是說,在這兩種情況下,都沒有數(shù)據(jù)傳輸。
絕對位置操作采用顯式元素索引,該操作不影響位置。如果索引參數(shù)超出限制,則絕對get操作和絕對put操作將拋出IndexOutOfBoundsException異常。
abstract ByteBuffer put(byte b)方法的作用:使用相對位置的put()操作,將給定的字節(jié)寫入此緩沖區(qū)的“當前位置”,然后該位置遞增。
abstract byte get()方法的作用:使用相對位置的get()操作,讀取此緩沖區(qū)“當前位置”的字節(jié),然后該位置遞增。
示例代碼如下:
public class Test3 { public static void main(String[] args) { ByteBuffer buffer1 = ByteBuffer.allocate(10); System.out.println( "A1 capacity=" + buffer1.capacity() + " limit=" + buffer1.limit() + " position=" + buffer1.position()); buffer1.put((byte) 125); System.out.println( "A2 capacity=" + buffer1.capacity() + " limit=" + buffer1.limit() + " position=" + buffer1.position()); buffer1.put((byte) 126); System.out.println( "A3 capacity=" + buffer1.capacity() + " limit=" + buffer1.limit() + " position=" + buffer1.position()); buffer1.put((byte) 127); System.out.println( "B capacity=" + buffer1.capacity() + " limit=" + buffer1.limit() + " position=" + buffer1.position()); buffer1.rewind(); System.out.println( "C capacity=" + buffer1.capacity() + " limit=" + buffer1.limit() + " position=" + buffer1.position()); System.out.println(buffer1.get()); System.out.println( "D capacity=" + buffer1.capacity() + " limit=" + buffer1.limit() + " position=" + buffer1.position()); System.out.println(buffer1.get()); System.out.println( "E capacity=" + buffer1.capacity() + " limit=" + buffer1.limit() + " position=" + buffer1.position()); System.out.println(buffer1.get()); System.out.println( "F capacity=" + buffer1.capacity() + " limit=" + buffer1.limit() + " position=" + buffer1.position()); System.out.println(buffer1.get()); byte[] getByteArray = buffer1.array(); for (int i = 0; i < getByteArray.length; i++) { System.out.print(getByteArray[i] + " - "); } } }
程序運行結(jié)果如下:
A1 capacity=10 limit=10 position=0 A2 capacity=10 limit=10 position=1 A3 capacity=10 limit=10 position=2 B capacity=10 limit=10 position=3 C capacity=10 limit=10 position=0 125 D capacity=10 limit=10 position=1 126 E capacity=10 limit=10 position=2 127 F capacity=10 limit=10 position=3 0 125-126-127-0-0-0-0-0-0-0-
從運行結(jié)果可以看出,在執(zhí)行相對位置讀或?qū)懖僮骱螅恢茫╬osition)呈遞增的狀態(tài),位置自動移動到下一個位置上,也就是位置的值是++position的效果,以便進行下一次讀或?qū)懖僮鳌?/p>
1.4.5 put(byte[] src, int offset, int length)和get(byte[] dst, int offset, int length)方法的使用
put(byte[] src, int offset, int length)方法的作用:相對批量put方法,此方法將把給定源數(shù)組中的字節(jié)傳輸?shù)酱司彌_區(qū)當前位置中。如果要從該數(shù)組中復(fù)制的字節(jié)多于此緩沖區(qū)中的剩余字節(jié)(即length > remaining()),則不傳輸字節(jié)且將拋出BufferOverflowException異常。否則,此方法將給定數(shù)組中的length個字節(jié)復(fù)制到此緩沖區(qū)中。將數(shù)組中給定off偏移量位置的數(shù)據(jù)復(fù)制到緩沖區(qū)的當前位置,從數(shù)組中復(fù)制的元素個數(shù)為length。換句話說,調(diào)用此方法的形式為dst.put(src, offset, length),效果與以下循環(huán)語句完全相同:
for (int i = offset; i < offset + length; i++) dst.put(a[i]);
區(qū)別在于它首先檢查此緩沖區(qū)中是否有足夠空間,這樣可能效率更高。
put(byte[] src, int offset, int length)方法的參數(shù)的介紹如下。
1)src:緩沖區(qū)中當前位置的數(shù)據(jù)來自于src數(shù)組。
2)offset:要讀取的第一個字節(jié)在“數(shù)組中的偏移量”,并“不是緩沖區(qū)的偏移”,必須為非負且不大于src.length。
3)length:要從給定數(shù)組讀取的字節(jié)的數(shù)量,必須為非負且不大于src.length-offset。
get(byte[] dst, int offset, int length)方法的作用:相對批量get方法,此方法將此緩沖區(qū)當前位置的字節(jié)傳輸?shù)浇o定的目標數(shù)組中。如果此緩沖中剩余的字節(jié)少于滿足請求所需的字節(jié)(即length > remaining()),則不傳輸字節(jié)且拋出BufferUnderflowException異常。否則,此方法將此緩沖區(qū)中的length個字節(jié)復(fù)制到給定數(shù)組中。從此緩沖區(qū)的當前位置和數(shù)組中的給定偏移量位置開始復(fù)制。然后,此緩沖區(qū)的位置將增加length。換句話說,調(diào)用此方法的形式為src.get(dst, off, len),效果與以下循環(huán)語句完全相同:
for (int i = offset; i < offset + length; i++) dst[i] = src.get();
區(qū)別在于get(byte[] dst, int offset, int length)方法首先檢查此緩沖區(qū)中是否具有足夠的字節(jié),這樣可能效率更高。
get(byte[] dst, int offset, int length)方法的參數(shù)介紹如下。
1)dst:將緩沖區(qū)中當前位置的數(shù)據(jù)寫入dst數(shù)組中。
2)offset:要寫入的第一個字節(jié)在“數(shù)組中的偏移量”,并“不是緩沖區(qū)的偏移”,必須為非負且不大于dst.length。
3)length:要寫入到給定數(shù)組中的字節(jié)的最大數(shù)量,必須為非負且不大于dst.length -offset。
下面來看看這兩個方法的基本使用情況,示例代碼如下:
public class Test5 { public static void main(String[] args) { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; byte[] byteArrayIn2 = { 55, 66, 77, 88 }; // 開辟10個空間 ByteBuffer bytebuffer = ByteBuffer.allocate(10); // 將1,2,3,4,5,6,7,8放入緩沖區(qū)的前8個位置中 bytebuffer.put(byteArrayIn1); // 執(zhí)行put()方法后位置發(fā)生改變,將位置設(shè)置成2 bytebuffer.position(2); // 將數(shù)組55,66,77,88中的66,77,88放入緩沖區(qū)的第3位 // 值變成1,2,66,77,88,6,7,8 //說明方法put(byte[] src, int offset, int length)放入的位置參考 // 的是Buffer當前的position位置 bytebuffer.put(byteArrayIn2, 1, 3); System.out.print("A="); byte[] getByte = bytebuffer.array(); for (int i = 0; i < getByte.length; i++) { System.out.print(getByte[i] + " "); } System.out.println(); bytebuffer.position(1); // 創(chuàng)建新的byte[]數(shù)組byteArrayOut,目的是將緩沖區(qū)中的數(shù)據(jù)導(dǎo)出來 byte[] byteArrayOut = new byte[bytebuffer.capacity()]; // 使用get()方法從緩沖區(qū)position值為1的位置開始,向byteArrayOut數(shù)組的 // 索引為3處一共復(fù)制4個字節(jié) //說明方法get(byte[] dst, int offset, int length)獲得數(shù)據(jù)的位置參考 // 的是Buffer當前的position位置 bytebuffer.get(byteArrayOut, 3, 4); System.out.print("B="); // 打印byteArrayOut數(shù)組中的內(nèi)容 for (int i = 0; i < byteArrayOut.length; i++) { System.out.print(byteArrayOut[i] + " "); } } }
程序運行結(jié)果如下:
A=1 2 66 77 88 6 7 8 0 0 B=0 0 0 2 66 77 88 0 0 0
在使用put(byte[] src, int offset, int length)方法的過程中,需要注意兩種出現(xiàn)異常的情況:
1)當offset+length的值大于src.length時,拋出IndexOutOfBoundsException異常;
2)當參數(shù)length的值大于buffer.remaining時,拋出BufferOverflowException異常。
也就是說,在上述兩種異常情況下都不傳輸字節(jié)。
先來測試第1種情況,代碼如下:
public class Test5_1 {
public static void main(String[] args) {
byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7 };
ByteBuffer bytebuffer = ByteBuffer.allocate(10);
bytebuffer.put(byteArrayIn1, 0, bytebuffer.capacity());
}
}
程序運行結(jié)果如下:
Exception in thread "main" java.lang.IndexOutOfBoundsException at java.nio.Buffer.checkBounds(Buffer.java:567) at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:187) at ByteBufferAPITest.Test5_1.main(Test5_1.java:9)
再來測試第2種情況,代碼如下:
public class Test5_2 {
public static void main(String[] args) {
byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7 };
ByteBuffer bytebuffer = ByteBuffer.allocate(10);
bytebuffer.position(9);
bytebuffer.put(byteArrayIn1, 0, 4);
}
}
程序運行結(jié)果如下:
Exception in thread "main" java.nio.BufferOverflowException at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:189) at ByteBufferAPITest.Test5_2.main(Test5_2.java:10)
在調(diào)用put(byte[] src, int offset, int length)方法時,如果遇到這種向緩沖區(qū)中寫入數(shù)據(jù)時有可能寫多或?qū)懮俚那闆r,那么可以使用如下的示例代碼進行解決:
public class Test5_3 { public static void main(String[] args) { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; ByteBuffer bytebuffer = ByteBuffer.allocate(10); int getArrayIndex = 0; while (getArrayIndex < byteArrayIn1.length) { // 下面代碼的作用就是判斷:緩沖區(qū)的剩余和數(shù)組的剩余誰少 int readLength = Math.min(bytebuffer.remaining(), byteArrayIn1.length - getArrayIndex); bytebuffer.put(byteArrayIn1, getArrayIndex, readLength); bytebuffer.flip(); byte[] getArray = bytebuffer.array(); for (int i = 0; i < bytebuffer.limit(); i++) { System.out.print(getArray[i] + " "); } getArrayIndex = getArrayIndex + readLength; System.out.println(); bytebuffer.clear(); } } }
程序運行結(jié)果如下:
1 2 3 4 5 6 7 8 9 10 11 12
上面的代碼在byte[]的length大于或等于緩沖區(qū)的remaining()時,或者小于或等于remaining()時都可以正確運行。
在使用get(byte[] dst, int offset, int length)方法的過程中,需要注意兩種出現(xiàn)異常的情況:
1)當offset+length的值大于dst.length時,拋出IndexOutOfBoundsException異常;
2)當參數(shù)length的值大于buffer.remaining時,拋出BufferUnderflowException異常。
也就是說,在上述兩種異常情況下都不傳輸字節(jié)。
先來測試第1種情況,代碼如下:
public class Test5_4 {
public static void main(String[] args) {
byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7 };
ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn1);
byte[] byteArrayOut = new byte[5];
bytebuffer.get(byteArrayOut, 0, 7);
}
}
運行效果如下:
Exception in thread "main" java.lang.IndexOutOfBoundsException at java.nio.Buffer.checkBounds(Buffer.java:567) at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:149) at ByteBufferAPITest.Test5_4.main(Test5_4.java:10)
再來測試第2種情況,代碼如下:
public class Test5_5 {
public static void main(String[] args) {
byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7 };
ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn1);
bytebuffer.position(5);
byte[] byteArrayOut = new byte[500];
bytebuffer.get(byteArrayOut, 0, 50);
}
}
運行效果如下:
Exception in thread "main" java.nio.BufferUnderflowException at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:151) at ByteBufferAPITest.Test5_5.main(Test5_5.java:11)
在調(diào)用get(byte[] dst, int offset, int length)方法時,如果遇到這種從緩沖區(qū)中獲得數(shù)據(jù)時有可能取多或取少的情況,那么可以使用如下的示例代碼進行解決:
public class Test5_6 { public static void main(String[] args) { byte[] byteArrayIn = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn); byte[] byteArrayOut = new byte[5]; while (bytebuffer.hasRemaining()) { int readLength = Math.min(bytebuffer.remaining(), byteArrayOut.length); bytebuffer.get(byteArrayOut, 0, readLength); for (int i = 0; i < readLength; i++) { System.out.print(byteArrayOut[i] + " "); } System.out.println(); } } }
運行效果如下:
1 2 3 4 5 6 7 8 9 10 11 12
總結(jié)一下本小節(jié)介紹的put(byte[] src, int offset, int length)和get(byte[] dst, int offset, int length)方法的執(zhí)行流程,核心流程代碼如下:
public ByteBuffer put(byte[] src, int offset, int length) { // 能從數(shù)組中取出指定長度的數(shù)據(jù),就不報錯(size是src數(shù)組的長度) if ((off | len | (off + len) | (size - (off + len))) < 0) throw new IndexOutOfBoundsException(); // 取出來的數(shù)據(jù)的大小小于或等于緩沖區(qū)的剩余空間,就不報錯 if (length > remaining()) throw new BufferOverflowException(); int end = offset + length; for (int i = offset; i < end; i++) this.put(src[i]); return this; }
get(byte[] dst, int offset, int length)方法和put(byte[] src, int offset, int length)方法的邏輯相同。
1.4.6 put(byte[] src)和get(byte[] dst)方法的使用
put(byte[] src)方法的作用:相對批量put方法,此方法將給定的源byte數(shù)組的所有內(nèi)容存儲到此緩沖區(qū)的當前位置中。與該方法功能完全相同的寫法為:dst.put(a, 0, a.length)。
get(byte[] dst)方法的作用:相對批量get方法,此方法將此緩沖區(qū)remaining的字節(jié)傳輸?shù)浇o定的目標數(shù)組中。與該方法功能完全相同的寫法為:src.get(a, 0, a.length)。使用此方法取得數(shù)據(jù)的數(shù)量取決于byte[] dst目標數(shù)組的大小。
put(byte[] src)和get(byte[] dst)方法調(diào)用的是3個參數(shù)的put和get方法,源代碼如下:
public final ByteBuffer put(byte[] src) { return put(src, 0, src.length); } public ByteBuffer get(byte[] dst) { return get(dst, 0, dst.length); }
在上述源代碼中調(diào)用的是3個參數(shù)的方法put(byte[] src, int offset, int length)和get(byte[] dst, int offset, int length)。
示例代碼如下:
public class Test4 { public static void main(String[] args) { byte[] byteArray = new byte[] { 3, 4, 5, 6, 7, 8 }; ByteBuffer buffer1 = ByteBuffer.allocate(10); buffer1.put((byte) 1); buffer1.put((byte) 2); System.out.println("A=" + buffer1.position()); buffer1.put(byteArray); // 是相對位置存入操作 System.out.println("B=" + buffer1.position()); buffer1.flip(); buffer1.position(3); System.out.println("C=" + buffer1.position()); byte[] newArray = new byte[buffer1.remaining()]; buffer1.get(newArray); // 是相對位置讀取操作 for (int i = 0; i < newArray.length; i++) { System.out.print(newArray[i] + " "); } } }
程序運行結(jié)果如下:
A=2 B=8 C=3 4 5 6 7 8
在使用put(byte[] src)和get(byte[] dst)方法的過程中,需要注意異常情況的發(fā)生。
(1)public final ByteBuffer put(byte[] src)
1)緩沖區(qū)的remaining大于或等于數(shù)組的length,不出現(xiàn)異常。
2)緩沖區(qū)的remaining小于數(shù)組的length,出現(xiàn)異常。
(2)public ByteBuffer get(byte[] dst)
1)緩沖區(qū)的remaining大于或等于數(shù)組的length,不出現(xiàn)異常。
2)緩沖區(qū)的remaining小于數(shù)組的length,出現(xiàn)異常。
下面對上述兩種方法中出現(xiàn)的兩種情況分別進行研究。
1)put(byte[] src)方法:緩沖區(qū)的remaining大于或等于數(shù)組的length,不出現(xiàn)異常。
public class Test4_1 { public static void main(String[] args) { byte[] byteArray = new byte[] { 1, 2, 3, 4, 5 }; ByteBuffer buffer = ByteBuffer.allocate(10); buffer.position(1); // 緩沖區(qū)的剩余空間足夠了,不出現(xiàn)異常 buffer.put(byteArray); byte[] newByteArray = buffer.array(); for (int i = 0; i < newByteArray.length; i++) { System.out.print(newByteArray[i]); } } }
程序運行結(jié)果如下:
0123450000
2)put(byte[] src)方法:緩沖區(qū)的remaining小于數(shù)組的length,出現(xiàn)異常。
public class Test4_2 {
public static void main(String[] args) {
byte[] byteArray = new byte[] { 3, 4, 5, 6, 7, 8 };
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.position(8); // 緩沖區(qū)的剩余空間不夠了,出現(xiàn)異常
buffer.put(byteArray);
}
}
程序運行結(jié)果如下:
Exception in thread "main" java.nio.BufferOverflowException at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:189) at java.nio.ByteBuffer.put(ByteBuffer.java:859) at ByteBufferAPITest.Test4_2.main(Test4_2.java:10)
3)get(byte[] dst)方法:緩沖區(qū)的remaining大于或等于數(shù)組的length,不出現(xiàn)異常。
public class Test4_3 { public static void main(String[] args) { byte[] byteArray1 = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; ByteBuffer buffer = ByteBuffer.wrap(byteArray1); byte[] byteArrayNew = new byte[5]; buffer.get(byteArrayNew); // 不出現(xiàn)異常 for (int i = 0; i < byteArrayNew.length; i++) { System.out.print(byteArrayNew[i]); } } }
程序運行結(jié)果如下:12345
4)get(byte[] dst)方法:緩沖區(qū)的remaining小于數(shù)組的length,出現(xiàn)異常。
public class Test4_4 {
public static void main(String[] args) {
byte[] byteArray1 = new byte[] { 1, 2, 3, 4, 5 };
ByteBuffer buffer = ByteBuffer.wrap(byteArray1);
buffer.position(3);
byte[] byteArrayNew = new byte[3];
buffer.get(byteArrayNew);
// 出現(xiàn)異常,因為緩沖區(qū)中的剩余數(shù)據(jù)不夠3個
}
}
程序運行結(jié)果如下:
Exception in thread "main" java.nio.BufferUnderflowException at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:151) at java.nio.ByteBuffer.get(ByteBuffer.java:715) at ByteBufferAPITest.Test4_4.main(Test4_4.java:11)
如果在使用public final ByteBuffer put(byte[] src)方法的過程中,出現(xiàn)字節(jié)數(shù)組的length大于或等于或者小于或等于緩沖區(qū)的remaining剩余空間時,就要進行特殊處理,即分批進行處理,示例代碼如下:
public class Test4_5 { public static void main(String[] args) throws NoSuchMethodException, Security Exception, IllegalAccessException, IllegalArgumentException, InvocationTargetException, Interrupted Exception { byte[] byteArray1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; ByteBuffer byteBuffer1 = ByteBuffer.allocate(4); int byteArrayCurrentIndex = 0; int byteArrayRemaining = 0; while (byteArrayCurrentIndex < byteArray1.length) { byteArrayRemaining = byteArray1.length - byteArrayCurrentIndex; int readLength = Math.min(byteArrayRemaining, byteBuffer1. remaining()); byte[] newByteArray = Arrays.copyOfRange(byteArray1, byteArray CurrentIndex, byteArrayCurrentIndex + readLength); byteBuffer1.put(newByteArray); byteBuffer1.flip(); byte[] getByte = byteBuffer1.array(); for (int i = 0; i < byteBuffer1.limit(); i++) { System.out.print(getByte[i] + " "); } System.out.println(); byteArrayCurrentIndex = byteArrayCurrentIndex + readLength; byteBuffer1.clear(); } } }
如果在使用get(byte[] dst)方法的過程中,出現(xiàn)字節(jié)數(shù)組的length大于或等于或者小于或等于緩沖區(qū)的remaining時,那么也要進行特殊處理,示例代碼如下:
public class Test4_6 { public static void main(String[] args) { byte[] byteArray = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; ByteBuffer buffer = ByteBuffer.wrap(byteArray); int copyDataCount = 3; while (buffer.hasRemaining()) { byte[] copyByteArray = new byte[Math.min(buffer.remaining(), copy DataCount)]; buffer.get(copyByteArray); for (int i = 0; i < copyByteArray.length; i++) { System.out.print(copyByteArray[i]); } System.out.println(); } } }
1.4.7 put(int index, byte b)和get(int index)方法的使用與position不變
put(int index, byte b)方法的作用:絕對put方法,將給定字節(jié)寫入此緩沖區(qū)的給定索引位置。
get(int index)方法的作用:絕對get方法,讀取指定位置索引處的字節(jié)。
示例代碼如下:
public class Test6_1 { public static void main(String[] args) { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; ByteBuffer bytebuffer = ByteBuffer.allocate(10); bytebuffer.put(byteArrayIn1); bytebuffer.put(2, (byte) 127); // System.out.println(bytebuffer.get(2)); // bytebuffer.position(0); byte[] byteArrayOut = new byte[bytebuffer.capacity()]; bytebuffer.get(byteArrayOut, 0, byteArrayOut.length); for (int i = 0; i < byteArrayOut.length; i++) { System.out.print(byteArrayOut[i] + " "); } } }
程序運行結(jié)果如下:
127 1 2127 4 5 6 7 8 0 0
使用絕對位置操作后,位置(position)并不改變,測試代碼如下:
public class Test6_2 { public static void main(String[] args) { ByteBuffer bytebuffer = ByteBuffer.allocate(10); bytebuffer.position(9); System.out.println(bytebuffer.position()); bytebuffer.put(2, (byte) 127); System.out.println(bytebuffer.position()); bytebuffer.rewind(); byte[] byteArrayOut = new byte[bytebuffer.capacity()]; bytebuffer.get(byteArrayOut, 0, byteArrayOut.length); for (int i = 0; i < byteArrayOut.length; i++) { System.out.print(byteArrayOut[i] + " "); } } }
程序運行結(jié)果如下:
9 9 0 0127 0 0 0 0 0 0 0
1.4.8 put(ByteBuffer src)方法的使用
put(ByteBuffer src)方法的作用:相對批量put方法,此方法將給定源緩沖區(qū)中的剩余字節(jié)傳輸?shù)酱司彌_區(qū)的當前位置中。如果源緩沖區(qū)中的剩余字節(jié)多于此緩沖區(qū)中的剩余字節(jié),即src.remaining() > remaining(),則不傳輸字節(jié)且拋出BufferOverflowException異常。否則,此方法將給定緩沖區(qū)中的n = src.remaining()個字節(jié)復(fù)制到此緩沖區(qū)中,從每個緩沖區(qū)的當前位置開始復(fù)制。然后,這兩個緩沖區(qū)的位置都增加n。
示例代碼如下:
public class Test7 { public static void main(String[] args) { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); byte[] byteArrayIn2 = { 55, 66, 77 }; ByteBuffer bytebuffer2 = ByteBuffer.wrap(byteArrayIn2); bytebuffer1.position(4); bytebuffer2.position(1); bytebuffer1.put(bytebuffer2); System.out.println("bytebuffer1被改變:" + bytebuffer1.position()); System.out.println("bytebuffer2被改變:" + bytebuffer2.position()); byte[] byteArrayOut = bytebuffer1.array(); for (int i = 0; i < byteArrayOut.length; i++) { System.out.print(byteArrayOut[i] + " "); } } }
程序運行結(jié)果如下:
bytebuffer1被改變:6 bytebuffer2被改變:3 1 2 3 4 66 77 7 8
1.4.9 putType()和getType()方法的使用
putChar(char value)方法的作用:用來寫入char值的相對put方法(可選操作)。將兩個包含指定char值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的當前位置,然后將該位置增加2。
putChar(int index, char value)方法的作用:用于寫入char值的絕對put方法(可選操作)。將兩個包含給定char值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的給定索引處。
putDouble(double value)方法的作用:用于寫入double值的相對put方法(可選操作)。將8個包含給定double值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的當前位置,然后將該位置增加8。
putDouble(int index, double value)方法的作用:用于寫入double值的絕對put方法(可選操作)。將8個包含給定double值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的給定索引處。
putFloat(float value)方法的作用:用于寫入float值的相對put方法(可選操作)。將4個包含給定float值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的當前位置,然后將該位置增加4。
putFloat(int index, float value)方法的作用:用于寫入float值的絕對put方法(可選操作)。將4個包含給定float值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的給定索引處。
putInt(int value)方法的作用:用于寫入int值的相對put方法(可選操作)。將4個包含給定int值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的當前位置,然后將該位置增加4。
putInt(int index, int value)方法的作用:用于寫入int值的絕對put方法(可選操作)。將4個包含給定int值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的給定索引處。
putLong(long value)方法的作用:用于寫入long值的相對put方法(可選操作)。將8個包含給定long值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的當前位置,然后將該位置增加8。
putLong(int index, long value)方法的作用:用于寫入long值的絕對put方法(可選操作)。將8個包含給定long值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的給定索引處。
putShort(short value)方法的作用:用于寫入short值的相對put方法(可選操作)。將兩個包含指定short值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的當前位置,然后將該位置增加2。
putShort(int index, short value)方法的作用:用于寫入short值的絕對put方法(可選操作)。將兩個包含給定short值的字節(jié)按照當前的字節(jié)順序?qū)懭氲酱司彌_區(qū)的給定索引處。
示例代碼如下:
public class Test8 { public static void main(String[] args) { ByteBuffer bytebuffer1 = ByteBuffer.allocate(100); bytebuffer1.putChar('a'); // 0-1, char占2個字節(jié) bytebuffer1.putChar(2, 'b'); //2-3 bytebuffer1.position(4); bytebuffer1.putDouble(1.1); // 4-11, double占8個字節(jié) bytebuffer1.putDouble(12, 1.2); //12-19 bytebuffer1.position(20); bytebuffer1.putFloat(2.1F); // 20-23, float占4個字節(jié) bytebuffer1.putFloat(24, 2.2F); //24-27 bytebuffer1.position(28); bytebuffer1.putInt(31); // 28-31, int占4個字節(jié) bytebuffer1.putInt(32, 32); //32-35 bytebuffer1.position(36); bytebuffer1.putLong(41L); // 36-43, long占8個字節(jié) bytebuffer1.putLong(44, 42L); //44-51 bytebuffer1.position(52); bytebuffer1.putShort((short) 51); //52-53, short占2個字節(jié) bytebuffer1.putShort(54, (short) 52); //54-55 bytebuffer1.position(0); byte[] byteArrayOut = bytebuffer1.array(); for (int i = 0; i < byteArrayOut.length; i++) { //System.out.print(byteArrayOut[i] + " "); } System.out.println(); System.out.println(bytebuffer1.getChar()); System.out.println(bytebuffer1.getChar(2)); bytebuffer1.position(4); System.out.println(bytebuffer1.getDouble()); System.out.println(bytebuffer1.getDouble(12)); bytebuffer1.position(20);
System.out.println(bytebuffer1.getFloat()); System.out.println(bytebuffer1.getFloat(24)); bytebuffer1.position(28); System.out.println(bytebuffer1.getInt()); System.out.println(bytebuffer1.getInt(32)); bytebuffer1.position(36); System.out.println(bytebuffer1.getLong()); System.out.println(bytebuffer1.getLong(44)); bytebuffer1.position(52); System.out.println(bytebuffer1.getShort()); System.out.println(bytebuffer1.getShort(54)); } }
程序運行結(jié)果如下:
a b 1.1 1.2 2.1 2.2 31 32 41 42 51 52
1.4.10 slice()方法的使用與arrayOffSet()為非0的測試
slice()方法的作用:創(chuàng)建新的字節(jié)緩沖區(qū),其內(nèi)容是此緩沖區(qū)內(nèi)容的共享子序列。新緩沖區(qū)的內(nèi)容將從此緩沖區(qū)的當前位置開始。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,反之亦然;這兩個緩沖區(qū)的位置、限制和標記值是相互獨立的。新緩沖區(qū)的位置將為0,其容量和限制將為此緩沖區(qū)中所剩余的字節(jié)數(shù)量,其標記是不確定的。當且僅當此緩沖區(qū)為直接緩沖區(qū)時,新緩沖區(qū)才是直接緩沖區(qū)。當且僅當此緩沖區(qū)為只讀時,新緩沖區(qū)才是只讀的。
示例代碼如下:
public class Test9_1 { public static void main(String[] args) { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); bytebuffer1.position(5); ByteBuffer bytebuffer2 = bytebuffer1.slice(); System.out.println("bytebuffer1.position=" + bytebuffer1.position() + " bytebuffer1.capacity=" + bytebuffer1.capacity() + " bytebuffer1.limit=" + bytebuffer1. limit()); System.out.println("bytebuffer2.position=" + bytebuffer2.position() + " bytebuffer2.capacity=" + bytebuffer2.capacity() + " bytebuffer2.limit=" + bytebuffer2. limit()); bytebuffer2.put(0, (byte) 111); byte[] byteArray1 = bytebuffer1.array(); byte[] byteArray2 = bytebuffer2.array(); for (int i = 0; i < byteArray1.length; i++) { System.out.print(byteArray1[i] + " "); } System.out.println(); for (int i = 0; i < byteArray2.length; i++) { System.out.print(byteArray2[i] + " "); } } }
程序運行結(jié)果如下:
bytebuffer1.position=5 bytebuffer1.capacity=8 bytebuffer1.limit=8 bytebuffer2.position=0 bytebuffer2.capacity=3 bytebuffer2.limit=3 1 2 3 4 5111 7 8 1 2 3 4 5111 7 8
在使用slice()方法后,再調(diào)用arrayOffSet()方法時,會出現(xiàn)返回值為非0的情況,測試代碼如下:
public class Test9_2 { public static void main(String[] args) { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); bytebuffer1.position(5); ByteBuffer bytebuffer2 = bytebuffer1.slice(); System.out.println(bytebuffer2.arrayOffset()); } }
程序運行結(jié)果如下:
5
運行結(jié)果說明bytebuffer2的第1個元素的位置是相對于byteArrayIn1數(shù)組中索引值為5的偏移。
1.4.11 轉(zhuǎn)換為CharBuffer字符緩沖區(qū)及中文的處理
asCharBuffer()方法的作用:創(chuàng)建此字節(jié)緩沖區(qū)的視圖,作為char緩沖區(qū)。新緩沖區(qū)的內(nèi)容將從此緩沖區(qū)的當前位置開始。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,反之亦然;這兩個緩沖區(qū)的位置、限制和標記值是相互獨立的。新緩沖區(qū)的位置將為0,其容量和限制將為此緩沖區(qū)中所剩余的字節(jié)數(shù)的1/2,其標記是不確定的。當且僅當此緩沖區(qū)為直接緩沖區(qū)時,新緩沖區(qū)才是直接緩沖區(qū)。當且僅當此緩沖區(qū)為只讀時,新緩沖區(qū)才是只讀的。
示例代碼如下:
public class Test10 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = "我是中國人".getBytes(); // 運行本代碼的*.java文件是UTF-8編碼,所以運行環(huán)境取得的編碼默認是UTF-8 System.out.println(Charset.defaultCharset().name()); ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn1); System.out.println("bytebuffer=" + bytebuffer.getClass().getName()); CharBuffer charBuffer = bytebuffer.asCharBuffer(); System.out.println("charBuffer=" + charBuffer.getClass().getName()); System.out.println("bytebuffer.position=" + bytebuffer.position() + " bytebuffer.capacity=" + bytebuffer.capacity() + " bytebuffer.limit=" + bytebuffer. limit()); System.out.println("charBuffer.position=" + charBuffer.position() + " charBuffer.capacity=" + charBuffer.capacity() + " charBuffer.limit=" + charBuffer. limit()); System.out.println(charBuffer.capacity()); charBuffer.position(0); for (int i = 0; i < charBuffer.capacity(); i++) { // get()方法使用的編碼為UTF-16BE // UTF-8與UTF-16BE并不是同一種編碼 // 所以這時出現(xiàn)了亂碼 System.out.print(charBuffer.get() + " "); } } }
上述程序運行結(jié)果如圖1-20所示。

圖1-20 運行結(jié)果
上面的代碼產(chǎn)生了4個步驟。
1)使用代碼語句“byte[] byteArrayIn1 = "我是中國人".getBytes(); ”將中文轉(zhuǎn)成字節(jié)數(shù)組,數(shù)組中存儲的編碼為UTF-8。
2)使用代碼語句“ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn1); ”將UTF-8編碼的字節(jié)數(shù)組轉(zhuǎn)換成字節(jié)緩沖區(qū)(ByteBuffer),緩沖區(qū)中存儲的編碼也為UTF-8。
3)使用代碼語句“CharBuffer charBuffer = bytebuffer.asCharBuffer(); ”將編碼格式為UTF-8的ByteBuffer中的內(nèi)容轉(zhuǎn)換成UTF-8編碼的CharBuffer。
4)當調(diào)用CharBuffer類的子類java.nio.ByteBufferAsCharBufferB中的get()方法時,以UTF-16BE的編碼格式獲得中文時出現(xiàn)編碼不匹配的情況,因此出現(xiàn)了亂碼。
從上面的運行結(jié)果來看,并沒有將正確的中文獲取出來,相反還出現(xiàn)了亂碼,出現(xiàn)亂碼的原因就是編碼不對稱造成的,解決辦法就是使編碼對稱,也就是將中文轉(zhuǎn)成字節(jié)數(shù)組時使用UTF-16BE編碼,而使用ByteBufferAsCharBufferB的get()方法時再以UTF-16BE編碼轉(zhuǎn)回中文即可,這樣就不會出現(xiàn)中文亂碼問題了。解決亂碼問題的程序代碼如下:
public class Test11 { public static void main(String[] args) throws UnsupportedEncodingException { // 將中文轉(zhuǎn)成UTF-16BE編碼的字節(jié)數(shù)組 byte[] byteArrayIn1 = "我是中國人".getBytes("utf-16BE"); System.out.println(Charset.defaultCharset().name()); ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn1); System.out.println("bytebuffer=" + bytebuffer.getClass().getName()); CharBuffer charBuffer = bytebuffer.asCharBuffer(); System.out.println("charBuffer=" + charBuffer.getClass().getName()); System.out.println("bytebuffer.position=" + bytebuffer.position() + " bytebuffer.capacity=" + bytebuffer.capacity() + " bytebuffer.limit=" + bytebuffer.limit()); System.out.println("charBuffer.position=" + charBuffer.position() + " charBuffer.capacity=" + charBuffer.capacity() + " charBuffer.limit=" + charBuffer.limit()); System.out.println(charBuffer.capacity()); charBuffer.position(0); for (int i = 0; i < charBuffer.capacity(); i++) { System.out.print(charBuffer.get() + " "); //UTF-16BE } } }
程序運行結(jié)果如下:
UTF-8 bytebuffer=java.nio.HeapByteBuffer charBuffer=java.nio.ByteBufferAsCharBufferB bytebuffer.position=0 bytebuffer.capacity=10 bytebuffer.limit=10 charBuffer.position=0 charBuffer.capacity=5 charBuffer.limit=5 5 我 是 中 國 人
從上面輸出的結(jié)果可以發(fā)現(xiàn),中文亂碼的問題解決了。
解決的步驟是什么呢?如下:
1)使用代碼語句“byte[] byteArrayIn1 = "我是中國人".getBytes("utf-16BE"); ”將中文轉(zhuǎn)成字節(jié)數(shù)組,數(shù)組中存儲的編碼為UTF-16BE。
2)使用代碼語句“ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn1); ”將UTF-16BE編碼的字節(jié)數(shù)組轉(zhuǎn)換成ByteBuffer, ByteBuffer中存儲的編碼也為UTF-16BE。
3)使用代碼語句“CharBuffer charBuffer = bytebuffer.asCharBuffer(); ”將編碼格式為UTF-16BE的ByteBuffer中的內(nèi)容轉(zhuǎn)換成UTF-16BE編碼的CharBuffer。
4)當調(diào)用CharBuffer類的子類java.nio.ByteBufferAsCharBufferB中的get()方法時,以UTF-16BE的編碼格式獲得中文時不再出現(xiàn)亂碼,這樣亂碼問題就解決了。
當然,還可以使用其他的辦法來解決亂碼問題。
示例代碼如下:
public class Test12 { public static void main(String[] args) throws UnsupportedEncodingException { // 字節(jié)數(shù)組使用的編碼為UTF-8 byte[] byteArrayIn1 = "我是中國人".getBytes("utf-8"); System.out.println(Charset.defaultCharset().name()); ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn1); // 將bytebuffer中的內(nèi)容轉(zhuǎn)成UTF-8編碼的CharBuffer CharBuffer charBuffer = Charset.forName("utf-8").decode(bytebuffer); System.out.println("bytebuffer=" + bytebuffer.getClass().getName()); System.out.println("charBuffer=" + charBuffer.getClass().getName()); System.out.println("bytebuffer.position=" + bytebuffer.position() + " bytebuffer.capacity=" + bytebuffer.capacity() + " bytebuffer.limit=" + bytebuffer.limit()); System.out.println("charBuffer.position=" + charBuffer.position() + " charBuffer.capacity=" + charBuffer.capacity() + " charBuffer.limit=" + charBuffer.limit()); System.out.println(charBuffer.capacity()); charBuffer.position(0); for (int i = 0; i < charBuffer.limit(); i++) { System.out.print(charBuffer.get() + " "); } } }
程序運行結(jié)果如下:
GBK bytebuffer=java.nio.HeapByteBuffer charBuffer=java.nio.HeapCharBuffer bytebuffer.position=15 bytebuffer.capacity=15 bytebuffer.limit=15 charBuffer.position=0 charBuffer.capacity=15 charBuffer.limit=5 15 我 是 中 國 人
輸出信息的第一行為GBK是因為*.java文件的編碼就是GBK,并沒有改成UTF-8。
上面的代碼也解決了中文亂碼問題,解決步驟如下。
1)使用代碼語句“byte[] byteArrayIn1 = "我是中國人".getBytes("utf-8"); ”將GBK編碼的中文轉(zhuǎn)換成UTF-8編碼格式的字節(jié)數(shù)組。
2)使用代碼語句“ByteBuffer bytebuffer = ByteBuffer.wrap(byteArrayIn1); ”將UTF-8編碼的字節(jié)數(shù)組轉(zhuǎn)換成ByteBuffer, ByteBuffer中存儲的編碼也為UTF-8。
3)使用代碼語句“CharBuffer charBuffer = Charset.forName("utf-8").decode(bytebuffer); ”的目的是將編碼類型為UTF-8的ByteBuffer轉(zhuǎn)換成編碼類型為UTF-8的java.nio.HeapCharBuffer。HeapCharBuffer類中的hb[]數(shù)組中存儲的內(nèi)容已經(jīng)是正確的UTF-8編碼的中文了。
4)當調(diào)用CharBuffer類的子類java.nio.HeapCharBuffer中的get()方法時,在hb[]數(shù)組中直接獲得中文,不再出現(xiàn)亂碼,亂碼問題解決了。
使用asCharBuffer()方法獲得CharBuffer后,對ByteBuffer的更改會直接影響CharBuffer中的值,示例代碼如下:
public class Test12_sameValue { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArray = "我是中國人".getBytes("utf-16BE"); ByteBuffer byteBuffer1 = ByteBuffer.wrap(byteArray); CharBuffer charBuffer = byteBuffer1.asCharBuffer(); byteBuffer1.put(2, "為".getBytes("utf-16BE")[0]); byteBuffer1.put(3, "為".getBytes("utf-16BE")[1]); charBuffer.clear(); for (int i = 0; i < charBuffer.limit(); i++) { System.out.print(charBuffer.get() + " "); } } }
運行結(jié)果如下:
我 為 中 國 人
1.4.12 轉(zhuǎn)換為其他類型的緩沖區(qū)
asDoubleBuffer()方法的作用:創(chuàng)建此字節(jié)緩沖區(qū)的視圖,作為double緩沖區(qū)。新緩沖區(qū)的內(nèi)容將從此緩沖區(qū)的當前位置開始。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,反之亦然;這兩個緩沖區(qū)的位置、限制和標記值是相互獨立的。新緩沖區(qū)的位置將為0,其容量和界限將為此緩沖區(qū)中所剩余的字節(jié)數(shù)的1/8,其標記是不確定的。當且僅當此緩沖區(qū)為直接緩沖區(qū)時,新緩沖區(qū)才是直接緩沖區(qū)。當且僅當此緩沖區(qū)為只讀時,新緩沖區(qū)才是只讀的。
asFloatBuffer()方法的作用:創(chuàng)建此字節(jié)緩沖區(qū)的視圖,作為float緩沖區(qū)。新緩沖區(qū)的內(nèi)容將從此緩沖區(qū)的當前位置開始。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,反之亦然;這兩個緩沖區(qū)的位置、限制和標記值是相互獨立的。新緩沖區(qū)的位置將為0,其容量和其限制將為此緩沖區(qū)中剩余字節(jié)數(shù)的1/4,其標記是不確定的。當且僅當此緩沖區(qū)為直接緩沖區(qū)時,新緩沖區(qū)才是直接緩沖區(qū)。當且僅當此緩沖區(qū)為只讀時,新緩沖區(qū)才是只讀的。
asIntBuffer()方法的作用:創(chuàng)建此字節(jié)緩沖區(qū)的視圖,作為int緩沖區(qū)。新緩沖區(qū)的內(nèi)容將從此緩沖區(qū)的當前位置開始。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,反之亦然;這兩個緩沖區(qū)的位置、限制和標記值是相互獨立的。新緩沖區(qū)的位置將為0,其容量和限制將為此緩沖區(qū)中所剩余的字節(jié)數(shù)的1/4,其標記是不確定的。當且僅當此緩沖區(qū)為直接緩沖區(qū)時,新緩沖區(qū)才是直接緩沖區(qū)。當且僅當此緩沖區(qū)為只讀時,新緩沖區(qū)才是只讀的。
asLongBuffer()方法的作用:創(chuàng)建此字節(jié)緩沖區(qū)的視圖,作為long緩沖區(qū)。新緩沖區(qū)的內(nèi)容將從此緩沖區(qū)的當前位置開始。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,反之亦然;這兩個緩沖區(qū)的位置、限制和標記值是相互獨立的。新緩沖區(qū)的位置將為0,其容量和限制將為此緩沖區(qū)中所剩余的字節(jié)數(shù)的1/8,其標記是不確定的。當且僅當此緩沖區(qū)為直接緩沖區(qū)時,新緩沖區(qū)才是直接緩沖區(qū)。當且僅當此緩沖區(qū)為只讀時,新緩沖區(qū)才是只讀的。
asShortBuffer()方法的作用:創(chuàng)建此字節(jié)緩沖區(qū)的視圖,作為short緩沖區(qū)。新緩沖區(qū)的內(nèi)容將從此緩沖區(qū)的當前位置開始。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,反之亦然;這兩個緩沖區(qū)的位置、限制和標記值是相互獨立的。新緩沖區(qū)的位置將為0,其容量和限制將為此緩沖區(qū)中所剩余的字節(jié)數(shù)的1/2,其標記是不確定的。當且僅當此緩沖區(qū)為直接緩沖區(qū)時,新緩沖區(qū)才是直接緩沖區(qū)。當且僅當此緩沖區(qū)為只讀時,新緩沖區(qū)才是只讀的。
示例代碼如下:
public class Test13 { public static void main(String[] args) throws UnsupportedEncodingException { ByteBuffer bytebuffer1 = ByteBuffer.allocate(32); bytebuffer1.putDouble(1.1D); bytebuffer1.putDouble(1.2D); bytebuffer1.putDouble(1.3D); bytebuffer1.putDouble(1.4D); bytebuffer1.flip(); DoubleBuffer doubleBuffer = bytebuffer1.asDoubleBuffer(); for (int i = 0; i < doubleBuffer.capacity(); i++) { System.out.print(doubleBuffer.get(i) + " "); } System.out.println(); ByteBuffer bytebuffer2 = ByteBuffer.allocate(16); bytebuffer2.putFloat(2.1F); bytebuffer2.putFloat(2.2F); bytebuffer2.putFloat(2.3F); bytebuffer2.putFloat(2.4F); bytebuffer2.flip(); FloatBuffer floatBuffer = bytebuffer2.asFloatBuffer(); for (int i = 0; i < floatBuffer.capacity(); i++) { System.out.print(floatBuffer.get(i) + " "); } System.out.println(); ByteBuffer bytebuffer3 = ByteBuffer.allocate(16); bytebuffer3.putInt(31); bytebuffer3.putInt(32); bytebuffer3.putInt(33); bytebuffer3.putInt(34); bytebuffer3.flip(); IntBuffer intBuffer = bytebuffer3.asIntBuffer(); for (int i = 0; i < intBuffer.capacity(); i++) { System.out.print(intBuffer.get(i) + " "); } System.out.println(); ByteBuffer bytebuffer4 = ByteBuffer.allocate(32); bytebuffer4.putLong(41L); bytebuffer4.putLong(42L); bytebuffer4.putLong(43L); bytebuffer4.putLong(44L); bytebuffer4.flip(); LongBuffer longBuffer = bytebuffer4.asLongBuffer(); for (int i = 0; i < longBuffer.capacity(); i++) { System.out.print(longBuffer.get(i) + " "); } System.out.println(); ByteBuffer bytebuffer5 = ByteBuffer.allocate(8); bytebuffer5.putShort((short) 51); bytebuffer5.putShort((short) 52L); bytebuffer5.putShort((short) 53L); bytebuffer5.putShort((short) 54L); bytebuffer5.flip(); ShortBuffer shortBuffer = bytebuffer5.asShortBuffer(); for (int i = 0; i < shortBuffer.capacity(); i++) { System.out.print(shortBuffer.get(i) + " "); } } }
程序運行結(jié)果如下:
1.1 1.2 1.3 1.4 2.1 2.2 2.3 2.4 31 32 33 34 41 42 43 44 51 52 53 54
在調(diào)用ByteBuffer中的putXXX()方法時,比如如下代碼:
ByteBuffer.putInt(value); ByteBuffer.getInt();
視圖緩沖區(qū)與之相比有以下三個優(yōu)勢:
1)視圖緩沖區(qū)不是根據(jù)字節(jié)進行索引,而是根據(jù)其特定于類型的值的大小進行索引;
2)視圖緩沖區(qū)提供了相對批量get和put方法,這些方法可在緩沖區(qū)和數(shù)組或相同類型的其他緩沖區(qū)之間傳輸值的連續(xù)序列;
3)視圖緩沖區(qū)可能更高效,這是因為當且僅當其支持的字節(jié)緩沖區(qū)為直接緩沖區(qū)時,它才是直接緩沖區(qū)。
先來驗證:視圖緩沖區(qū)不是根據(jù)字節(jié)進行索引,而是根據(jù)其特定于類型的值的大小進行索引。
示例代碼如下:
public class Test13_1 { public static void main(String[] args) throws UnsupportedEncodingException { ByteBuffer bytebuffer = ByteBuffer.allocate(10); System.out.println("A1=" + bytebuffer.position()); bytebuffer.putInt(123); System.out.println("A2=" + bytebuffer.position()); bytebuffer.putInt(456); System.out.println("A3=" + bytebuffer.position()); System.out.println(); IntBuffer intBuffer = IntBuffer.allocate(10); System.out.println("B1=" + intBuffer.position()); intBuffer.put(456); System.out.println("B2=" + intBuffer.position()); intBuffer.put(789); System.out.println("B3=" + intBuffer.position()); } }
程序運行后的結(jié)果如下:
A1=0 A2=4 A3=8 B1=0 B2=1 B3=2
從輸出的結(jié)果來看,ByteBuffer是按字節(jié)為單位進行存儲,而IntBuffer是按數(shù)據(jù)類型為單位進行存儲。
再來驗證:視圖緩沖區(qū)提供了相對批量get和put方法,這些方法可在緩沖區(qū)和數(shù)組或相同類型的其他緩沖區(qū)之間傳輸值的連續(xù)序列。
示例代碼如下:
public class Test13_2 { public static void main(String[] args) throws UnsupportedEncodingException { ByteBuffer bytebuffer = ByteBuffer.allocate(10); bytebuffer.putInt(123); bytebuffer.putInt(456); bytebuffer.flip(); System.out.println("bytebuffer position=" + bytebuffer.position() + " value=" + bytebuffer.getInt()); System.out.println("bytebuffer position=" + bytebuffer.position() + " value=" + bytebuffer.getInt()); System.out.println("bytebuffer position=" + bytebuffer.position()); System.out.println(); IntBuffer intBuffer = IntBuffer.allocate(10); intBuffer.put(456); intBuffer.put(789); intBuffer.flip(); System.out.println("intBuffer position=" + intBuffer.position() + " value=" + intBuffer.get()); System.out.println("intBuffer position=" + intBuffer.position() + " value=" + intBuffer.get()); System.out.println("intBuffer position=" + intBuffer.position()); } }
程序運行結(jié)果如下:
bytebuffer position=0 value=123 bytebuffer position=4 value=456 bytebuffer position=8 intBuffer position=0 value=456 intBuffer position=1 value=789 intBuffer position=2
最后驗證:視圖緩沖區(qū)可能更高效,這是因為當且僅當其支持的字節(jié)緩沖區(qū)為直接緩沖區(qū)時,它才是直接緩沖區(qū)。
示例代碼如下:
public class Test13_3 { public static void main(String[] args) throws UnsupportedEncodingException { ByteBuffer bytebuffer = ByteBuffer.allocateDirect(100); bytebuffer.putInt(123); bytebuffer.putInt(456); bytebuffer.flip(); IntBuffer intBuffer = bytebuffer.asIntBuffer(); System.out.println(intBuffer.get()); System.out.println(intBuffer.get()); System.out.println(); System.out.println("bytebuffer是直接緩沖區(qū),效率比較快:"); System.out.println(bytebuffer); System.out.println("由于bytebuffer是直接的,所以intBuffer也是直接緩沖區(qū)了:"); System.out.println(intBuffer); } }
程序運行結(jié)果如下:
123 456 bytebuffer是直接緩沖區(qū),效率比較快: java.nio.DirectByteBuffer[pos=0 lim=8 cap=100] 由于bytebuffer是直接的,所以intBuffer也是直接緩沖區(qū)了: java.nio.DirectIntBufferS[pos=2 lim=2 cap=2]
1.4.13 設(shè)置與獲得字節(jié)順序
order()方法與字節(jié)數(shù)據(jù)排列的順序有關(guān),因為不同的CPU在讀取字節(jié)時的順序是不一樣的,有的CPU從高位開始讀,而有的CPU從低位開始讀,當這兩種CPU傳遞數(shù)據(jù)時就要將字節(jié)排列的順序進行統(tǒng)一,此時order(ByteOrder bo)方法就有用武之地了,它的作用就是設(shè)置字節(jié)的排列順序。
什么是高位和低位呢?如果是16位(雙字節(jié))的數(shù)據(jù),如FF1A,高位是FF,低位是1A。如果是32位的數(shù)據(jù),如3F68415B,高位字是3F68,低位字是415B,右邊是低位,左邊是高位。
ByteOrder order()方法的作用:獲取此緩沖區(qū)的字節(jié)順序。新創(chuàng)建的字節(jié)緩沖區(qū)的順序始終為BIG_ENDIAN。在讀寫多字節(jié)值以及為此字節(jié)緩沖區(qū)創(chuàng)建視圖緩沖區(qū)時,使用該字節(jié)順序。
1)public static final ByteOrder BIG_ENDIAN:表示BIG-ENDIAN字節(jié)順序的常量。按照此順序,多字節(jié)值的字節(jié)順序是從最高有效位到最低有效位的。
2)public static final ByteOrder LITTLE_ENDIAN:表示LITTLE-ENDIAN字節(jié)順序的常量。按照此順序,多字節(jié)值的字節(jié)順序是從最低有效位到最高有效位的。
order(ByteOrder bo)方法的作用:修改此緩沖區(qū)的字節(jié)順序,在默認的情況下,字節(jié)緩沖區(qū)的初始順序始終是BIG_ENDIAN。
示例代碼如下:
public class Test14 { public static void main(String[] args) throws UnsupportedEncodingException { int value = 123456789; ByteBuffer bytebuffer1 = ByteBuffer.allocate(4); System.out.print(bytebuffer1.order() + " "); System.out.print(bytebuffer1.order() + " "); bytebuffer1.putInt(value); byte[] byteArray = bytebuffer1.array(); for (int i = 0; i < byteArray.length; i++) { System.out.print(byteArray[i] + " "); } System.out.println(); bytebuffer1 = ByteBuffer.allocate(4); System.out.print(bytebuffer1.order() + " "); bytebuffer1.order(ByteOrder.BIG_ENDIAN); System.out.print(bytebuffer1.order() + " "); bytebuffer1.putInt(value); byteArray = bytebuffer1.array(); for (int i = 0; i < byteArray.length; i++) { System.out.print(byteArray[i] + " "); } System.out.println(); bytebuffer1 = ByteBuffer.allocate(4); System.out.print(bytebuffer1.order() + " "); bytebuffer1.order(ByteOrder.LITTLE_ENDIAN); System.out.print(bytebuffer1.order() + " "); bytebuffer1.putInt(value); byteArray = bytebuffer1.array(); for (int i = 0; i < byteArray.length; i++) { System.out.print(byteArray[i] + " "); } } }
程序運行結(jié)果如下:
BIG_ENDIAN BIG_ENDIAN 7 91-51 21 BIG_ENDIAN BIG_ENDIAN 7 91-51 21 BIG_ENDIAN LITTLE_ENDIAN 21-51 91 7
如果字節(jié)順序不一致,那么在獲取數(shù)據(jù)時就會出現(xiàn)錯誤的值,示例代碼如下:
public class Test14_1 { public static void main(String[] args) throws UnsupportedEncodingException { ByteBuffer byteBuffer1 = ByteBuffer.allocate(8); byteBuffer1.order(ByteOrder.BIG_ENDIAN); byteBuffer1.putInt(123); byteBuffer1.putInt(567); byteBuffer1.flip(); System.out.println(byteBuffer1.getInt()); System.out.println(byteBuffer1.getInt()); ByteBuffer byteBuffer2 = ByteBuffer.wrap(byteBuffer1.array()); byteBuffer2.order(ByteOrder.LITTLE_ENDIAN); System.out.println(byteBuffer2.getInt()); System.out.println(byteBuffer2.getInt()); } }
運行結(jié)果就是錯誤的值:
123 567 2063597568 922877952
1.4.14 創(chuàng)建只讀緩沖區(qū)
asReadOnlyBuffer()方法的作用:創(chuàng)建共享此緩沖區(qū)內(nèi)容的新的只讀字節(jié)緩沖區(qū)。新緩沖區(qū)的內(nèi)容將為此緩沖區(qū)的內(nèi)容。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,但新緩沖區(qū)將是只讀的并且不允許修改共享內(nèi)容。兩個緩沖區(qū)的位置、限制和標記值是相互獨立的。新緩沖區(qū)的容量、限制、位置和標記值將與此緩沖區(qū)相同。
示例代碼如下:
public class Test15 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn = { 1, 2, 3, 4, 5 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn); ByteBuffer bytebuffer2 = bytebuffer1.asReadOnlyBuffer(); System.out.println("bytebuffer1.isReadOnly()=" + bytebuffer1.isReadOnly()); System.out.println("bytebuffer2.isReadOnly()=" + bytebuffer2.isReadOnly()); bytebuffer2.rewind(); bytebuffer2.put((byte) 123); } }
程序運行結(jié)果如下:
bytebuffer1.isReadOnly()=false bytebuffer2.isReadOnly()=true Exception in thread "main" java.nio.ReadOnlyBufferException at java.nio.HeapByteBufferR.put(HeapByteBufferR.java:172) at ByteBufferAPITest.Test15.main(Test15.java:14)
1.4.15 壓縮緩沖區(qū)
compact()方法的作用:壓縮此緩沖區(qū)(可選操作),將緩沖區(qū)的當前位置和限制之間的字節(jié)(如果有)復(fù)制到緩沖區(qū)的開始處,即將索引p = position()處的字節(jié)復(fù)制到索引0處,將索引p + 1處的字節(jié)復(fù)制到索引1處,依此類推,直到將索引limit() -1處的字節(jié)復(fù)制到索引n = limit() -1- p處。然后,將緩沖區(qū)的位置設(shè)置為n+1,并將其限制設(shè)置為其容量。如果已定義了標記,則丟棄它。將緩沖區(qū)的位置設(shè)置為復(fù)制的字節(jié)數(shù),而不是0,以便調(diào)用此方法后可以緊接著調(diào)用另一個相對put方法。
壓縮compact執(zhí)行的過程如圖1-21所示。

圖1-21 壓縮compact執(zhí)行的過程
將緩沖區(qū)中的數(shù)據(jù)寫出之后調(diào)用此方法,以防寫出不完整。例如,以下循環(huán)語句通過buf緩沖區(qū)將字節(jié)從一個端點復(fù)制到另一個端點:
buf.clear(); //還原緩沖區(qū)的狀態(tài) while (in.read(buf) >= 0 || buf.position ! = 0) { buf.flip(); out.write(buf); buf.compact(); //執(zhí)行壓縮操作 }
示例代碼如下:
public class Test16 { public static void main(String[] args) throws UnsupportedEncodingException { ByteBuffer byteBuffer1 = ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5, 6 }); System.out.println("A capacity=" + byteBuffer1.capacity() + " position=" + byteBuffer1.position() + " limit=" + byteBuffer1.limit()); System.out.println("1 getValue=" + byteBuffer1.get()); System.out.println("B capacity=" + byteBuffer1.capacity() + " position=" + byteBuffer1.position() + " limit=" + byteBuffer1.limit()); System.out.println("2 getValue=" + byteBuffer1.get()); System.out.println("C capacity=" + byteBuffer1.capacity() + " position=" + byteBuffer1.position() + " limit=" + byteBuffer1.limit()); byteBuffer1.compact(); System.out.println("byteBuffer1.compact()"); System.out.println("D capacity=" + byteBuffer1.capacity() + " position=" + byteBuffer1.position() + " limit=" + byteBuffer1.limit()); byte[] getByteArray = byteBuffer1.array(); for (int i = 0; i < getByteArray.length; i++) { System.out.print(getByteArray[i] + " "); } } }
程序運行結(jié)果如下:
A capacity=6 position=0 limit=6 1 getValue=1 B capacity=6 position=1 limit=6 2 getValue=2 C capacity=6 position=2 limit=6 byteBuffer1.compact() D capacity=6 position=4 limit=6 3 4 5 6 5 6
可以在使用完compact()方法后再使用flip()方法讀取壓縮后的數(shù)據(jù)內(nèi)容。
1.4.16 比較緩沖區(qū)的內(nèi)容
比較緩沖區(qū)的內(nèi)容是否相同有兩種方法:equals()和compareTo()。這兩種方法還是有使用細節(jié)上的區(qū)別,先來看一下ByteBuffer類中的equals()方法的源代碼:
public boolean equals(Object ob) {
if (this == ob)
return true;
if (! (ob instanceof ByteBuffer))
return false;
ByteBuffer that = (ByteBuffer)ob;
if (this.remaining() ! = that.remaining())
return false;
int p = this.position();
for (int i = this.limit() -1, j = that.limit() -1; i >= p; i--, j--)
if (! equals(this.get(i), that.get(j)))
return false;
return true;
}
從equals()方法的源代碼中可以分析出運算的4個主要邏輯。
1)判斷是不是自身,如果是自身,則返回為true。
2)判斷是不是ByteBuffer類的實例,如果不是,則返回false。
3)判斷remaining()值是否一樣,如果不一樣,則返回false。
4)判斷兩個緩沖區(qū)中的position與limit之間的數(shù)據(jù)是否完全一樣,只要有一個字節(jié)不同,就返回false,否則返回true。
通過源代碼來看,兩個緩沖區(qū)的capacity可以不相同,說明equals()方法比較的是position到limit的內(nèi)容是否完全一樣。
1)驗證:判斷是不是自身,如果是自身,則返回為true。
示例代碼如下:
public class Test17_1 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); System.out.println("A=" + bytebuffer1.equals(bytebuffer1)); } }
程序運行結(jié)果如下:
A=true
2)驗證:判斷是不是ByteBuffer類的實例,如果不是,則返回false。
示例代碼如下:
public class Test17_2 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5 }; int[] intArrayIn2 = { 1, 2, 3, 4, 5 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); IntBuffer intbuffer2 = IntBuffer.wrap(intArrayIn2); System.out.println("A=" + bytebuffer1.equals(intbuffer2)); } }
程序運行結(jié)果如下:
A=false
3)驗證:判斷remaining()值是否一樣,如果不一樣,則返回false。
示例代碼如下:
public class Test17_3 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 3, 4, 5 }; byte[] byteArrayIn2 = { 1, 2, 3, 4, 5, 6, 7, 8 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); ByteBuffer bytebuffer2 = ByteBuffer.wrap(byteArrayIn2); bytebuffer1.position(0); bytebuffer2.position(3); System.out.println("A=" + bytebuffer1.equals(bytebuffer2)); System.out.println("bytebuffer1.remaining()=" + bytebuffer1.remaining()); System.out.println("bytebuffer2.remaining()=" + bytebuffer2.remaining()); } }
程序運行結(jié)果如下:
A=false bytebuffer1.remaining()=3 bytebuffer2.remaining()=5
4)驗證:判斷兩個緩沖區(qū)中的position與limit之間的數(shù)據(jù)是否完全一樣,只要有一個字節(jié)不同,就返回false,否則返回true。
示例代碼如下:
public class Test17_4 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 3, 4, 5 }; byte[] byteArrayIn2 = { 1, 2, 3, 4, 5, 6, 7, 8 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); ByteBuffer bytebuffer2 = ByteBuffer.wrap(byteArrayIn2); bytebuffer1.position(0); bytebuffer1.limit(3); bytebuffer2.position(2); bytebuffer2.limit(5); System.out.println("A=" + bytebuffer1.equals(bytebuffer2)); System.out.println("AA1 bytebuffer1.remaining()=" + bytebuffer1.remaining()); System.out.println("AA2 bytebuffer2.remaining()=" + bytebuffer2.remaining()); bytebuffer2.put(3, (byte) 44); System.out.println("B=" + bytebuffer1.equals(bytebuffer2)); System.out.println("BB1 bytebuffer1.remaining()=" + bytebuffer1.remaining()); System.out.println("BB2 bytebuffer2.remaining()=" + bytebuffer2.remaining()); } }
程序運行結(jié)果如下:
A=true AA1 bytebuffer1.remaining()=3 AA2 bytebuffer2.remaining()=3 B=false BB1 bytebuffer1.remaining()=3 BB2 bytebuffer2.remaining()=3
以上的示例展示了equals(Object object)方法的使用。
compareTo(ByteBuffer that)方法的作用:將此緩沖區(qū)與另一個緩沖區(qū)進行比較。比較兩個字節(jié)緩沖區(qū)的方法是按字典順序比較它們的剩余元素序列,而不考慮每個序列在其對應(yīng)緩沖區(qū)中的起始位置。該方法的源代碼如下:
public int compareTo(ByteBuffer that) { int n = this.position() + Math.min(this.remaining(), that.remaining()); for (int i = this.position(), j = that.position(); i < n; i++, j++) { int cmp = compare(this.get(i), that.get(j)); if (cmp ! = 0) return cmp; } return this.remaining() - that.remaining(); }
從compareTo(ByteBuffer that)方法的源代碼中可以分析出運算的3個主要邏輯。
1)判斷兩個ByteBuffer的范圍是從當前ByteBuffer對象的當前位置開始,以兩個ByteBuffer對象最小的remaining()值結(jié)束說明判斷的范圍是remaining的交集。
2)如果在開始與結(jié)束的范圍之間有一個字節(jié)不同,則返回兩者的減數(shù),Byte類中的源代碼如下:
public static int compare(byte x, byte y) { return x - y; }
3)如果在開始與結(jié)束的范圍之間每個字節(jié)都相同,則返回兩者remaining()的減數(shù)。
通過源代碼來看,兩個緩沖區(qū)的capacity可以不相同,這個特性和equals()方法一致。
1)驗證:如果在開始與結(jié)束的范圍之間有一個字節(jié)不同,則返回兩者的減數(shù)。
示例代碼如下:
public class Test17_5 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 3, 4, 5 }; byte[] byteArrayIn2 = { 1, 2, 3, 104, 5, 6, 7, 8, 9, 10 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); ByteBuffer bytebuffer2 = ByteBuffer.wrap(byteArrayIn2); bytebuffer1.position(0); bytebuffer2.position(2); System.out.println("A=" + bytebuffer1.compareTo(bytebuffer2)); } }
程序運行結(jié)果如下:
A=-100
2)驗證:如果在開始與結(jié)束的范圍之間每個字節(jié)都相同,則返回兩者remaining()的減數(shù)。
示例代碼如下:
public class Test17_6 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 3, 4, 5 }; byte[] byteArrayIn2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); ByteBuffer bytebuffer2 = ByteBuffer.wrap(byteArrayIn2); bytebuffer1.position(0); bytebuffer2.position(2); System.out.println("A=" + bytebuffer1.compareTo(bytebuffer2)); } }
程序運行結(jié)果如下:
A=-5
通過查看equals(Object obj)和compareTo(ByteBuffer that)方法的源代碼可以發(fā)現(xiàn),這兩個方法的邏輯就是當前position到limit之間的字符是否逐個相同。
1.4.17 復(fù)制緩沖區(qū)
ByteBuffer duplicate()方法的作用:創(chuàng)建共享此緩沖區(qū)內(nèi)容的新的字節(jié)緩沖區(qū)。新緩沖區(qū)的內(nèi)容將為此緩沖區(qū)的內(nèi)容。此緩沖區(qū)內(nèi)容的更改在新緩沖區(qū)中是可見的,反之亦然。在創(chuàng)建新的緩沖區(qū)時,容量、限制、位置和標記的值將與此緩沖區(qū)相同,但是這兩個緩沖區(qū)的位置、界限和標記值是相互獨立的。當且僅當此緩沖區(qū)為直接緩沖區(qū)時,新緩沖區(qū)才是直接緩沖區(qū)。當且僅當此緩沖區(qū)為只讀時,新緩沖區(qū)才是只讀的。
下面的示例代碼演示了duplicate()方法與slice()方法的區(qū)別。
public class Test18 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); bytebuffer1.position(2); System.out.println("bytebuffer1 capacity=" + bytebuffer1.capacity() + " limit=" + bytebuffer1.limit() + " position=" + bytebuffer1.position()); ByteBuffer bytebuffer2 = bytebuffer1.slice(); ByteBuffer bytebuffer3 = bytebuffer1.duplicate(); // bytebuffer4和bytebuffer1指向的地址是一個 // 所以在debug中的id是一樣的 ByteBuffer bytebuffer4 = bytebuffer1; System.out.println("bytebuffer2 capacity=" + bytebuffer2.capacity() + " limit=" + bytebuffer2.limit() + " position=" + bytebuffer2.position()); System.out.println("bytebuffer3 capacity=" + bytebuffer3.capacity() + " limit=" + bytebuffer3.limit() + " position=" + bytebuffer3.position()); bytebuffer2.position(0); for (int i = bytebuffer2.position(); i < bytebuffer2.limit(); i++) { System.out.print(bytebuffer2.get(i) + " "); } System.out.println(); bytebuffer3.position(0); for (int i = bytebuffer3.position(); i < bytebuffer3.limit(); i++) { System.out.print(bytebuffer3.get(i) + " "); } } }
程序運行結(jié)果如下:
bytebuffer1 capacity=5 limit=5 position=2 bytebuffer2 capacity=3 limit=3 position=0 bytebuffer3 capacity=5 limit=5 position=2 3 4 5 1 2 3 4 5
duplicate()方法和slice()方法都會創(chuàng)建新的緩沖區(qū)對象,效果如圖1-22所示。

圖1-22 新創(chuàng)建的緩沖區(qū)對象
使用duplicate()方法和slice()方法能創(chuàng)建新的緩沖區(qū),但這些新緩沖區(qū)使用的還是原來緩沖區(qū)中的byte[]字節(jié)數(shù)組。
下面驗證使用duplicate()方法創(chuàng)建新的緩沖區(qū)后,在新緩沖區(qū)中添加數(shù)據(jù)時,被復(fù)制的緩沖區(qū)中的值也發(fā)生改變,說明這兩個緩沖區(qū)用的是同一個byte[],代碼如下:
public class Test19 { public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); ByteBuffer bytebuffer2 = bytebuffer1.duplicate(); System.out.println("A capacity=" + bytebuffer1.capacity() + " position=" + bytebuffer1.position() + " limit=" + bytebuffer1.limit()); System.out.println("B capacity=" + bytebuffer2.capacity() + " position=" + bytebuffer2.position() + " limit=" + bytebuffer2.limit()); bytebuffer2.put(1, (byte) 22); bytebuffer2.position(3); System.out.println("C capacity=" + bytebuffer1.capacity() + " position=" + bytebuffer1.position() + " limit=" + bytebuffer1.limit()); System.out.println("D capacity=" + bytebuffer2.capacity() + " position=" + bytebuffer2.position() + " limit=" + bytebuffer2.limit() + " bytebuffer2位置是3,而bytebuffer1還是 0,說明位置、限制和標記值是獨立的"); bytebuffer1.position(0); for (int i = 0; i < bytebuffer1.limit(); i++) { System.out.print(bytebuffer1.get(i) + " "); } } }
程序運行結(jié)果如下:
A capacity=5 position=0 limit=5 B capacity=5 position=0 limit=5 C capacity=5 position=0 limit=5 D capacity=5 position=3 limit=5 bytebuffer2位置是3,而bytebuffer1還是0,說明位置、 限制和標記值是獨立的 1 22 3 4 5
1.4.18 對緩沖區(qū)進行擴容
一旦創(chuàng)建緩沖區(qū),則容量(capacity)就不能被改變。如果想對緩沖區(qū)進行擴展,就得進行相應(yīng)的處理,示例代碼如下:
public class Test20 { public static ByteBuffer extendsSize(ByteBuffer buffer, int extendsSize) { ByteBuffer newBytebuffer = ByteBuffer.allocate(buffer.capacity() + extendsSize); newBytebuffer.put(buffer); return newBytebuffer; } public static void main(String[] args) throws UnsupportedEncodingException { byte[] byteArrayIn1 = { 1, 2, 3, 4, 5 }; ByteBuffer bytebuffer1 = ByteBuffer.wrap(byteArrayIn1); ByteBuffer bytebuffer2 = extendsSize(bytebuffer1, 2); byte[] newArray = bytebuffer2.array(); for (int i = 0; i < newArray.length; i++) { System.out.print(newArray[i] + " "); } } }
程序運行結(jié)果如下:
1 2 3 4 5 0 0
- The Complete Rust Programming Reference Guide
- 數(shù)據(jù)庫系統(tǒng)教程(第2版)
- Java程序設(shè)計:原理與范例
- 零基礎(chǔ)入門學(xué)習(xí)Python(第2版)
- 大數(shù)據(jù)分析與應(yīng)用實戰(zhàn):統(tǒng)計機器學(xué)習(xí)之數(shù)據(jù)導(dǎo)向編程
- Java零基礎(chǔ)實戰(zhàn)
- Web性能實戰(zhàn)
- Access 2010數(shù)據(jù)庫應(yīng)用技術(shù)實驗指導(dǎo)與習(xí)題選解(第2版)
- 深度探索Go語言:對象模型與runtime的原理特性及應(yīng)用
- Mastering Adobe Captivate 7
- Python函數(shù)式編程(第2版)
- 百萬在線:大型游戲服務(wù)端開發(fā)
- 讓Python遇上Office:從編程入門到自動化辦公實踐
- Moodle 3.x Developer's Guide
- HTML5+CSS+JavaScript深入學(xué)習(xí)實錄