ByteBuffer 使用
- 1 java.nio包中的類定義的緩沖區類型
- 2 緩沖區常用屬性
- 2.1緩沖區的容量(capacity)
- 2.2 緩沖區的位置(position)
- 2.3 緩沖區的限制(limit)
- 2.4 緩沖區的標記(mark)
- 2.5 剩余容量 remaining/hasRemaining
- 3 緩沖區常用方法
- 3.1 創建緩沖區
- 3.1.1 allocate方法
- 3.1.2 wrap通過封裝數組來創建緩沖區
- 3.2 創建視圖緩沖區
- 3.3. 復制緩沖區 duplicate
- 3.4 切分緩沖區slice
- 3.5 標記和重置mark和reset
- 3.6 翻轉flip
- 3.7 清除clear
- 3.8 壓縮compact
- 3.9 rewind(倒回)
- 3.10 寫數據
- 3.11 讀數據
- 4 常用使用方式
- 4.1 讀寫范式
- 4.2 與字符串之間的轉換
- 參見java7入門經典
1 java.nio包中的類定義的緩沖區類型
類 | 描述 |
---|---|
ByteBuffer | 用來存儲 byte 類型值的緩沖區,也可以在這種緩沖區中存儲任意其他基本類型的進制值(boolean 類型除外)。存儲的每個二進制值在緩沖區中占據的字節長度根據類型長度決定char 或short 類型值占據2字節int 類型值占據4字節,等等 |
CharBuffer | 只存儲char類型值的緩沖區 |
ShortBuffer | 只存儲short 類型值的緩沖區 |
IntBuffer | 只存儲int類型值的緩沖區 |
LongBuffer | 只存儲 long 類型值的緩沖區 |
Doublebuffer | 只存儲double類型值的緩沖區 |
雖然有不同的類可以定義緩沖區,但是只使用 BvteBuffer 類型的緩沖區來讀寫其他類型的緩沖區被稱為視圖緩沖區,因為通常將它們創建為已有 ByteBuffer 類型緩沖區的視圖視圖緩沖區提供了一種容易的方式可以將各種類型的數據項從 ByteBuffer 中讀出或者寫入到其中
2 緩沖區常用屬性
2.1緩沖區的容量(capacity)
緩沖區的容量是指緩沖區所能包含的值的最大數目而不是字節數目(除ByteBuffer外)創建緩沖區時,緩沖區的容量是固定的并且不能在隨后進行修改。通過調用從 Buffer 類繼承而來的 capacity0方法,可以獲取 int 類型的緩沖區對象的容量。
2.2 緩沖區的位置(position)
位置(position)是指可用于讀或寫的下一個緩沖區元素的索引位置
2.3 緩沖區的限制(limit)
限制(limit)是指緩沖區中第一個不應該被讀或寫的值的索引位置,所以從 position 所指的元素到 limit-1所指的元素都可以被讀取或寫入。如果想要填充緩沖區,位置必須是零,因為這是第一個數據項所要存儲的位置,而且限制必須等于緩沖區的容量,因為最后一個數據項必須存儲到緩沖區的最后一個元素中,值為 capacity-1。
2.4 緩沖區的標記(mark)
標記(mark)記錄當前position的值, 默認值-1。和reset方法結合使用,position被改變后,可以通過調用reset() 方法恢復到mark的位置
2.5 剩余容量 remaining/hasRemaining
//Returns the number of elements between the current position and the limit.
//Returns:The number of elements remaining in this buffer
public final int remaining() {return limit - position;}
//Tells whether there are any elements between the current position and the limit.
public final boolean hasRemaining() {return position < limit;}
3 緩沖區常用方法
3.1 創建緩沖區
3.1.1 allocate方法
定義緩沖區的這些類都沒有可用的公共構造函數。作為替代,可以使用靜態的工廠方法來創建緩沖區。通常會通過調用類的靜態 allocate0方法來創建 ByteBuffer 類型的緩沖區對象。將int類型值作為參數傳入定義緩沖區容量的方法一一緩沖區必須加載的最大字節數。
//創建緩沖區(java 的堆內存)位置為0并且容量為 1024,限制未1024
ByteBuffer buf = ByteBuffer.allocate(1024);
//Allocates a new direct byte buffer.操作系統直接內存(非java堆內存,創建成本高,如果進行IO、Socket操作,讀取效率要高)
ByteBuffer bufdir = ByteBuffer.allocateDirect(1024);
當使用緩沖區類的 allocate0方法創建新緩沖區時,位置為零并且限制被設置為新緩沖區的容量
3.1.2 wrap通過封裝數組來創建緩沖區
通過調用靜態 wrap()方法中的一種來封裝已有的與緩沖區元素類型相同的數組,也能創建緩沖,使用這種方法創建的緩沖區已經包含數組中的數據。可以通過封裝 bytel類型的數組來創建ByteBuffer 對象。
當通過封裝數組來創建緩沖區時,緩沖區對象沒有自己的內存來存儲數據。緩沖區被放在用來定義它的數組的背后,所以對緩沖區中值的修改也會修改數組,反之也一樣。緩沖區的容量和限制被設置為數組的長度,位置為零
- 無參wrap()
byte[] bytes= {1,2,3};
ByteBuffer buffer = ByteBuffer.wrap(bytes);
- 有參 wrap(byte[] array, int offset, int length)
String saying = "Handsome is as handsome does.";// Get string as byte arraybyte[] array = saying.getBytes();ByteBuffer buf = ByteBuffer.wrap(array, 9,14);
緩沖區的容量是 array.length,并且位置被設置為第二個參數的值 9。第三個參數設定要讀或寫的緩沖區元素的數目,這個值與位置相加就可以定義緩沖區的限制。
3.2 創建視圖緩沖區
// Buffer of 1024 bytes capacity
ByteBuffer buf = ByteBuffer.allocate(20);
buf.put((byte)1);
buf.put((byte)2);
// Now create a view buffer,
IntBuffer intBuf buf.asIntBuffer();
視圖緩沖區的內容都起始于原始字節緩沖區的當前位置。視圖緩沖區本身的位置被初始設置為零,而且容量和限制被設置為由原始字節緩沖區剩余的字節數(limit-position)除以視圖緩沖區存儲的元素類型所占的字節數目而得到的值。視圖緩沖區 的position、limit 與原始字節緩沖區position、limit 相互獨立。
3.3. 復制緩沖區 duplicate
通過調用緩沖區的 duplicate()方法可以復制任何討論過的緩沖區。該方法返回一個指向緩沖區的引用,其類型與原始緩沖區一樣,并且共享原始緩沖區的內容和內存。復制緩沖區與原始緩沖區擁有同樣的容量、位置和限制(duplicate時刻只是值一樣,兩個彼此獨立)。但是,雖然對復制內容的修改會反映到原始緩沖區,而且反之也一樣,但是原始緩沖區與復制緩沖區的位置和限制都彼此獨立。使用復制緩沖區的一種情況是當并行訪問緩沖區的不同部分,這使得在復制緩沖區中,能以不影響原始緩沖區的任何方式來提取訪問緩沖區內容的不同部分。
因此,復制緩沖區實際上并不真的是內存中的一個新緩沖區,它只是一個新對象,能提供訪問用于緩存數據的同一塊內存的另一種方式。duplicate0方法返回的指向新對象的通用類型與原始對象的一樣,但是沒有任何獨立的數據存儲。這里只是共享屬于原始緩存區對象的內存,但是使用獨立的位置和限制值。
3.4 切分緩沖區slice
由 slice()方法生成的緩沖區會映射到原始緩沖區的一部分-從當前位置開始直到 imit-1的所有元素(包括 limit-1 處的元素)。當然,如果原始緩沖區對象的位置為零并且限制與容量相等,那么slice0方法將產生與 duplicate0方法一樣的結果一緩沖區內存被共享。切分緩沖區實際上是通過兩個或更多的途徑來給予訪問緩沖區中給定部分數據的權限,使得切分后的每個緩沖區都有自己獨立的位置和限制(調用slice后,新的緩沖區position=0,limit/capacity=原始緩沖區limit-position)
3.5 標記和重置mark和reset
對緩沖區使用標記屬性是為了記錄緩沖區中想要稍后返回的特定索引位置。通過調用從 Buffer類繼承而來的緩沖區對象的 mark0方法,可以將標記設置為當前位置。例如:
buf.position(32);
// Mark the current position 32
buf.mark();
在經過一系列修改位置的操作之后,可以通過調用從 Buffer 類繼承而來的 reset0方法,將緩沖區的位置重新設置為之前的標記:
//一系列buf 的操作
// Reset position to last marked 32
buf.reset();
3.6 翻轉flip
flip將limit設置為當前位置,然后將位置設置回0
flip 方法源代碼
public final Buffer flip() {limit = position;position = 0;mark = -1;return this;}
flip 方法通常用于由寫模式轉換為讀模式即 完成寫操作后 需要讀取操作前調用flip
上圖是完成寫操作,然后調用flip
limit = position;position = 0;
3.7 清除clear
clear0方法將限制設置為容量,將位置設置為零,所以能夠將這些值存儲為之前創建緩沖區時
它們的狀態,但是這不會重新設置緩沖區中的數據,內容會保持不變。如果要重新設置數據,就必
須將新數據傳輸到緩沖區中。在想要重用緩沖區時通常需要調用 clear0方法
clear 源代碼
public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;}
clear 之后
3.8 壓縮compact
compact 將position到limit 之間的數據拷貝到從頭部(0開始-limit-position)
完成后 position=limit-position. limit=capacity
常用于 讀模式切換為寫模式
3.9 rewind(倒回)
rewind0方法只是簡單地重新將位置設置為零,但不改變限制
rewind 源代碼
//Rewinds this buffer. The position is set to zero and the mark is discarded.
public final Buffer rewind() {position = 0;mark = -1;return this;}
3.10 寫數據
- 調用 channel 的 read 方法
- 調用 buffer 自己的 put 方法
//從channel 中讀取數據寫入buf,buf 的 position 會向后移動 readBytes 位
int readBytes = channel.read(buf);
//position 會向后移動1位
buf.put((byte)127);
方法 | 描述 |
---|---|
put(byte b) | 將參數指定的字節傳輸到緩沖區的當前位置并且將位置增加 1如果緩沖區的位置不比限制小,將會拋出BuferOverflowException 類型的異常 |
put(int index, byte b) | 將第二個參數指定的字節傳輸到緩沖區中由第一個參數指定的索引位置。緩沖區的位置不會改變。如果索引值為負,或者如果索引值大于或等于緩沖區的限制,將拋出IndexOutOfBoundsException 類型的異常 |
put(byte[] array) | 將 array 數組中的所有元素都傳輸到緩沖區中從當前位置開始的位置。位置被增加的值為數組的長度。如果緩沖區中沒有足夠的空間來存儲數組的內容,將會拋出BufferOverflowException 類型的異常 |
put(bytel] array, int offset, int length) | 從aray[offset]到array[offset+length-1](包括這兩個字節)的字節傳輸到緩沖區中從當前位置開始的位置,位置被增加的值為length。如果緩沖區中沒有足夠的空間來存儲它們,將會拋出BufferOverflowExcepiton類型的異常 |
put(ByteBuffer src) | 將src中保留的字節即src 中從當前索引位置到limit一1位置的元素傳輸到緩沖區中(從當前位置開始的位置,位置被增加的值為 src.remaining()),。如果存儲這些素的空間不夠,將會拋出BufficrOverflowException類型的異常。如果src與當前緩沖區一樣嘗試將級沖區傳輸到自身,將會拋出IlegalArgumentException 類型的異常 |
3.11 讀數據
- 調用 channel 的 write 方法
- 調用 buffer 自己的 get 方法
//從buf中讀取數據,寫入channel 中,position 會向后移動 writeBytes 位,注意寫后調取 buf.hasRemaining判斷是否buf 數據全部寫入channel 中(受channel 緩沖區的限制,不一定一次能把buf 中剩余的的數據全部寫入channel 中)
int writeBytes = channel.write(buf);
//position 會向后移動1位
byte b = buf.get();
方法 | 描述 |
---|---|
get() | 提取并返回當前緩沖區位置所在的字節并且增加位置的值 |
get(int index) | 返回索引位置index上的字節,position 不變 |
get(byte[]bytes) | 從緩沖區的位置0開始,往后提取 byteslength 個字節對緩沖區的位置值增加bytes.length,然后返回指向當前緩沖區的引用。如果緩沖區中可用的字節數少于bytes.length,將拋出 BufferUnderflowException 類型的異常 |
get(byte[] bytes,int offset, int length) | 從當前緩沖區位置開始,往后提取 length 個字節并且將它們存儲在 bytes 數組中從索引位置ofset開始的地方。緩沖區的位置值會增加 length,指向當前緩沖區的用會被返回。如果可用的字節數少于 length,將會拋出 BufferUnderflowException類型的異常;如果 ofset 和/或 length 的值導致一個非法的數組索引,將會拋出IndexOutOfBoundsException 類型的異常 |
4 常用使用方式
4.1 讀寫范式
- 向 buffer 寫入數據,例如調用 channel.read(buffer)
- 調用 flip() 切換至讀模式
- 從 buffer 讀取數據,例如調用 buffer.get()
- 調用 clear() 或 compact() 切換至寫模式
- 重復 1~4 步驟
4.2 與字符串之間的轉換
編碼:字符串調用getByte方法獲得byte數組,將byte數組放入ByteBuffer中
解碼:先調用ByteBuffer的flip方法,然后通過StandardCharsets的decoder方法解碼
public class Translate {public static void main(String[] args) {// 準備兩個字符串String str1 = "hello";// 方式1 通過字符串的getByte方法獲得字節數組,放入緩沖區中ByteBuffer buffer1 = ByteBuffer.allocate(16);buffer1.put(str1.getBytes());// 切換模式buffer1.flip();// 方式2 ByteBuffer buffer1 = ByteBuffer.wrap(str1.getBytes()); //此方式不用flip 因為wrap返回的buf position =0// 通過StandardCharsets解碼,獲得CharBuffer,再通過toString獲得字符串String str2 = StandardCharsets.UTF_8.decode(buffer1).toString();System.out.println(str2);}
}