在NIO網絡編程中,通道直接從ByteBuffer中讀取數據。Buffer類及其子類定義了一個用於處理數據緩沖區的api。Buffer類定義了所有的緩沖區都具有的四個屬性來提供關於其所包含的數據元素的信息。
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
下面詳細介紹下
屬性 | 描述 |
---|---|
Capacity | 容量,即可以容納的最大數據量;在緩沖區創建時被設定並且不能改變 |
Limit | 表示緩沖區的當前終點,不能對緩沖區超過極限的位置進行讀寫操作。且極限是可以修改的 |
Position | 位置,下一個要被讀或寫的元素的索引,每次讀寫緩沖區數據時都會改變改值,為下次讀寫作准備 |
Mark | 標記,調用mark()來設置mark=position,再調用reset()可以讓position恢復到標記的位置 |
緩沖區相關api
public abstract class Buffer {
//JDK1.4時,引入的api
public final int capacity( )//返回此緩沖區的容量
public final int position( )//返回此緩沖區的位置
public final Buffer position (int newPositio)//設置此緩沖區的位置
public final int limit( )//返回此緩沖區的限制
public final Buffer limit (int newLimit)//設置此緩沖區的限制
public final Buffer mark( )//在此緩沖區的位置設置標記
public final Buffer reset( )//將此緩沖區的位置重置為以前標記的位置
public final Buffer clear( )//清除此緩沖區
public final Buffer flip( )//反轉此緩沖區
public final Buffer rewind( )//重繞此緩沖區
public final int remaining( )//返回當前位置與限制之間的元素數
public final boolean hasRemaining( )//告知在當前位置和限制之間是否有元素
public abstract boolean isReadOnly( );//告知此緩沖區是否為只讀緩沖區
//JDK1.6時引入的api
public abstract boolean hasArray();//告知此緩沖區是否具有可訪問的底層實現數組
public abstract Object array();//返回此緩沖區的底層實現數組
public abstract int arrayOffset();//返回此緩沖區的底層實現數組中第一個緩沖區元素的偏移量
public abstract boolean isDirect();//告知此緩沖區是否為直接緩沖區
}
Buffer類的7種基本數據類型(ByteBuffer、CharBuffer、IntBuffer、ShortBuffer、LongBuffer、DoubleBuffer、FloatBuffer)的緩沖區實現都是抽象的。且父類(Buffer)也沒有提供存取函數。
public abstract class CharBuffer
extends Buffer
implements Comparable<CharBuffer>, Appendable, CharSequence, Readable
{}
在子類中提供了靜態工廠方法來創建相應類的實例,以及get、put操作來實現緩存區的存取。
public abstract class ByteBuffer {
//緩沖區創建相關api
public static ByteBuffer allocateDirect(int capacity)//創建直接緩沖區
public static ByteBuffer allocate(int capacity)
public static ByteBuffer wrap(byte[] array)
public static ByteBuffer wrap(byte[] array,int offset, int length)//構造初始化位置offset和上界length的緩沖區
//緩存區存取相關API
public abstract byte get( );//從當前位置position上get,get之后,position會自動+1
public abstract byte get (int index);//從絕對位置get
public abstract ByteBuffer put (byte b);//從當前位置上普通,put之后,position會自動+1
public abstract ByteBuffer put (int index, byte b);//從絕對位置上put
}
兩大種創建緩沖對象的區別在於是否分配空間來存儲數據。
allocate方法創建並分配一個私有的空間來儲存指定容量大小的數據元素。
wrap方法會創建一個緩沖區對象,但不分配任何空間來存儲數據元素。構造器的入參參數數組來存儲緩沖區的數據元素。
下面是創建緩沖區對象的demo
//方式1:allocate方式直接分配,內部將隱含的創建一個數組
ByteBuffer allocate = ByteBuffer.allocate(10);
//方式2:通過wrap根據一個已有的數組創建
byte[] bytes=new byte[10];
ByteBuffer wrap = ByteBuffer.wrap(bytes);
//方式3:通過wrap根據一個已有的數組指定區間創建
ByteBuffer wrapoffset = ByteBuffer.wrap(bytes,2,5);
下面詳細介紹下經常用到的方法
·flip()
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
其實就是buffer.limit(buffer.position()).position(0);將一個能夠繼續添加數據元素的填充狀態的緩沖區翻轉成一個准備讀出元素的釋放狀態。
·remaining()返回從當前位置到上限的元素個數
/**
* Returns the number of elements between the current position and the
* limit. </p>
*
* @return The number of elements remaining in this buffer
*/
public final int remaining() {
return limit - position;
}
· hasRemaining()返回是否已達到上限
/**
* Tells whether there are any elements between the current position and
* the limit. </p>
*
* @return <tt>true</tt> if, and only if, there is at least one element
* remaining in this buffer
*/
public final boolean hasRemaining() {
return position < limit;
}
·compact()將所有未讀數據拷貝到Buffer起始處,丟棄已經釋放的數據,保留未釋放的數據,然后將position設置到最后一個未讀元素的正后面。
public ByteBuffer compact() {
int pos = position();
int lim = limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
unsafe.copyMemory(ix(pos), ix(0), rem << 0);
position(rem);
limit(capacity());
discardMark();
return this;
}
復制緩沖區
最后講一下緩沖區的復制。緩沖區的復制有分兩種:
1、完全復制:調用duplicate()函數或者asReadOnlyBuffer()函數
2、部分復制:調用slice函數
duplicate()函數創建了一個與原始緩沖區相似的新緩沖區。兩個緩沖區共享數據元素,擁有同樣的容量,但每個緩沖區擁有各自的位置,上界和標記屬性。對一個緩沖區內的數據元素所做的改變會反映在另外一個緩沖區上。這一副本緩沖區具有與原始緩沖區同樣的數據視圖。如果原始的緩沖區為只讀,或者為直接緩沖區,新的緩沖區將繼承這些屬性。
CharBuffer buffer = CharBuffer.allocate (8);
buffer.position (3).limit (6).mark( ).position (5);
CharBuffer dupeBuffer = buffer.duplicate( );
buffer.clear( );