官术网_书友最值得收藏!

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
主站蜘蛛池模板: 乌兰县| 阳泉市| 康平县| 延寿县| 芒康县| 鹤壁市| 黄大仙区| 博罗县| 葵青区| 平顶山市| 区。| 广灵县| 长垣县| 涿鹿县| 灵宝市| 凤庆县| 张家口市| 塔城市| 准格尔旗| 石城县| 宁明县| 灵台县| 嘉峪关市| 五寨县| 闵行区| 罗源县| 武宣县| 来宾市| 泗水县| 永川市| 水城县| 定结县| 斗六市| 保山市| 枣阳市| 邢台县| 长沙市| 临清市| 稷山县| 汶川县| 梅河口市|