亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Java ByteBuffer 分析

1. 前言

基于 java.net.Socket 進行 TCP Socket 編程,我們是通過 java.net.SocketInputStream 和 java.net.SocketOutputStream 實現數據讀寫,這是基于字節流的數據讀寫,每次讀寫固定的幾個字節,沒有緩沖能力。為了提高 I/O 讀寫性能,我們引入了 BufferedInputStream & BufferedOutputStream。緩沖流本質是提供了一個較大塊內存,實現大塊數據讀寫的能力。Java NIO 提供的 SocketChannel 是基于 ByteBuffer 實現數據讀寫的,天生具有緩沖能力,畢竟 Java NIO 就是為了解決性能問題的嘛。如果從 SocketChannel 讀取數據,必須預先分配好 ByteBuffer;如果要想將數據寫入 SocketChannel,必須預先將數據寫入 ByteBuffer。

Java NIO 定義了一個關于緩沖的抽象類是 java.nio.Buffer,Buffer 有很多實現子類,ByteBuffer 僅僅是其中一個子類。下來我們就對 java.nio.ByteBuffer 的功能做一個介紹。

2. java.nio.Buffer 基本結構

java.nio.Buffer 是一個抽象類,定義了 Buffer 的基本結構。Buffer 存放的內容是 Java 的基本類型,針對每一個基本類型,都有一個實現類。比如,LongBuffer,IntBuffer,ByteBuffer 等。Buffer 是一個線性結構,內部實現是一個數組,是有大小限制的。java.nio.Buffer 中定義了幾個非常重要的屬性,聲明如下:

private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
  • capacity,表示 ByteBuffer 的容量,即 Buffer 總大小。
  • position,表示 Buffer 當前數據讀寫的位置。position 的取值不會是負數,也不會超過 limit 的取值。
  • limit,表示 Buffer 讀寫操作的結束位置。limit 的取值不會是負數,也不會超過 capacity 的取值。
  • mark,用于用戶自定義的標記位置。

3. java.nio.ByteBuffer 介紹

java.nio.ByteBuffer 中存儲的內容是 java 的基本類型 byte。一個非空的 ByteBuffer 的結構如下:

圖片描述

ByteBuffer 內部維護了一個 byte 數組,position、limit、capacity 都是數組的下標。

3.1 ByteBuffer 創建

ByteBuffer 本身也是一個抽象類,沒有提供 public 構造方法,創建 ByteBuffer 必須通過它的工廠方法完成。如果是要創建一個新的、空的 ByteBuffer,可以調用它的 allocate 方法。如果是想把一個已知的 byte 數組包裝到 ByteBuffer 中,而不是重新分配內存空間,可以調用它的 wrap 方法。聲明如下:

public static ByteBuffer allocate(int capacity) 
public static ByteBuffer wrap(byte[] array, int offset, int length)
public static ByteBuffer wrap(byte[] array)

allocate 方法包含一個 capacity 參數,需要用戶指定 ByteBuffer 的大小。 wrap 方法有兩個重載實現,都需要傳入數組的引用,另外一個還可以指定一個 offset 和 length。示例代碼如下:

ByteBuffer newBuffer = ByteBuffer.allocate(1024);       

byte[] tmpByteArray = new byte[512];
ByteBuffer wrapBuffer = ByteBuffer.wrap(tmpByteArray);        

新創建的 ByteBuffer,capacity、position、limit 的取值如下圖所示:

圖片描述

position 指向數組開頭,capacity 和 limit 都指向數組結尾。

3.2 向 ByteBuffer 寫入數據

ByteBuffer 提供了一組重載的、寫入數據的方法,你可以寫入單個 byte,也可以寫入一個 byte 數組。聲明如下:

   public abstract ByteBuffer put(byte b);
   public final ByteBuffer put(byte[] src)

示例代碼如下:

tmpByteArray[0] = (byte)0x11;
tmpByteArray[1] = (byte)0x22;
newBuffer.put((byte)0xAA);
newBuffer.put((byte)0xBB);
newBuffer.put(tmpByteArray, 0, 2);

經過 put 操作后, newBuffer 的 capacity、position、limit 的值如下圖所示:
圖片描述

當我們向 newBuffer 中 put 4 個 byte 類型的數據后,position 指向 4,capacity 和 limit 沒有變化。

3.3 ByteBuffer 的 flip 方法

如果要從一個寫入數據的 ByteBuffer 讀取數據,需要調用 ByteBuffer 的 flip 方法。flip 方法會改變 capacity、position、limit 的值。示例代碼:

newBuffer.flip();

調用完 flip 方法后,newBuffer 的 capacity、position、limit 的值如下圖所示:

圖片描述

3.4 從 ByteBuffer 讀取數據

ByteBuffer 提供了一組重載的、讀取數據的方法,你可以讀取單個 byte,也可以讀取一個 byte 數組。聲明如下:

   public abstract byte get();
   public ByteBuffer get(byte[] dst, int offset, int length)

示例代碼如下:

newBuffer.get();
newBuffer.get(tmpByteArray, 0, 2);

經過 gett 操作后, newBuffer 的 capacity、position、limit 的值如下圖所示:
圖片描述

3.5 ByteBuffer 的 rewind 方法

ByteBuffer 的 rewind 方法僅僅是將 position 設置為 0。

3.6 ByteBuffer 的 compat 方法

ByteBuffer 的 compat 方法將 position 所指向的、長度為 limit - position 的數據拷貝到數組的開頭,然后重新設定 position 和 limit 的值。compat 方法是非常有用的,比如你在解析讀取的報文的時候,如果消息不完整,你可以調用 compat 方法,然后繼續接收。

4. 總結

本小節主要是介紹了 ByteBuffer 的常見用法。要想熟練的從事 Java NIO 編程,首先必須理解 ByteBuffer 的原理和基本用法。ByteBuffer 的本質就是維護了一個數組,通過 position、limit、capacity 來維護讀寫的位置。另外,網絡編程是需要關注字節序的,ByteBuffer 默認是網絡字節序,你可以調用它的 order 方法設置字節序。