我們知道NIO的三大核心是buffer,channel和selector,本文開始詳細介紹下buffer
緩衝區Buffer
1.緩衝區介紹
一個Buffer對象是固定數量的數據的容器。其作用是一個存儲器,或者分段運輸區,在這裏數據可被存儲並在之後用於檢索。緩衝區可以寫滿和釋放。對於每個非布爾原始數據類型都有一個緩衝區類。儘管緩衝區作用於它們存儲的原始數據類型,但緩衝區十分傾向於處理字節。
緩衝區的工作與通道緊密聯繫。通道是 I/O 傳輸發生時通過的入口,而緩衝區是這些數據傳輸的來源或目標。
下圖是Buffer的類層次圖。在頂部是通用Buffer類,Buffer 定義所有緩衝區類型共有的操作,無論是它們所包含的數據類型還是可能具有的特定行爲。這一共同點將會成爲我們的出發點。
2.緩衝區操作
概念上,緩衝區是包在一個對象內的基本數據元素數組。Buffer 類相比一個簡單數組的優點是它將關於數據的數據內容和信息包含在一個單一的對象中。Buffer 類以及它專有的子類定義了一個用於處理數據緩衝區的 API。
2.1 創建緩衝區
新的緩衝區是由分配或包裝操作創建的.
方式 說明
分配 創建一個緩衝區對象並分配一個私有的空間來存儲容量大小的數據元素
包裝 創建一個緩衝區對象但不分配任何空間來存儲數據元素,
使用我們單獨提供的數據作爲存儲空間來存儲緩衝區的數據元素
分配方式:
// 創建一個ByteBuffer,容量爲10
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
1
2
包裝方式:
byte[] array = new byte[10];
ByteBuffer.wrap(array);
1
2
2.2 屬性
所有的緩衝區都具有四個屬性來提供關於其所包含的數據元素的信息。
屬性 說明
容量(Capacity) 緩衝區能夠容納的數據元素的最大數量,緩衝區創建時被設定,永遠不能被改變
上界(Limit) 緩衝區第一個不能被讀或寫的元素,或者說緩衝區中現存元素的計數
位置(Position) 下一個要被讀或寫的元素的索引,位置會自動由相應的get()和put()方法更新
標記(Mark) 一個備忘位置,調用mark()方法來設定,mark=position,調用reset方法設定position=mark。標記在設定前是未定義的(undefined)
四個屬性之前總是遵循以下關係:
mark <= position <= limit <= capacity
1
舉例:
// 創建一個ByteBuffer,容量爲10
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
1
2
位置被設爲0,而且容量和上界被設爲10,剛好經過緩衝區能夠容納的最後一個字節。標記最初未定義。容量是固定的,但另外的三個屬性可以在使用緩衝區時改變
2.3 緩衝區API介紹
接下來我們先看下Buffer中提供的方法
package java.nio;
public abstract class Buffer {
public final int capacity( )
public final int position( )
public final Buffer position (int newPosition)
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( );
}
put方法
'存取’也就將數據保存到緩衝區中及從緩衝區中取出數據,在Buffer類中並沒有提供get和put方法,這兩個方法在具體的Buffer子類中有提供,比如ByteBuffer.如下
public abstract class ByteBuffer
extends Buffer implements Comparable
{
// This is a partial API listing
public abstract byte get( );
public abstract byte get (int index);
public abstract ByteBuffer put (byte b);
public abstract ByteBuffer put (int index, byte b);
}
保存數據到緩衝區
public static void main(String[] args) {
// 創建一個ByteBuffer,容量爲10
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
// 看一下初始時4個核心變量的值
System.out.println("初始時-->limit--->" + byteBuffer.limit());
System.out.println("初始時-->position--->" + byteBuffer.position());
System.out.println("初始時-->capacity--->" + byteBuffer.capacity());
System.out.println("初始時-->mark--->" + byteBuffer.mark());
System.out.println("--------------------------------------");
// 添加一些數據到緩衝區中
String s = "bobo";
byteBuffer.put(s.getBytes());
// 看一下初始時4個核心變量的值
System.out.println("put完之後-->limit--->" + byteBuffer.limit());
System.out.println("put完之後-->position--->" + byteBuffer.position());
System.out.println("put完之後-->capacity--->" + byteBuffer.capacity());
System.out.println("put完之後-->mark--->" + byteBuffer.mark());
}
輸出:
初始時-->limit--->10
初始時-->position--->0
初始時-->capacity--->10
初始時-->mark--->java.nio.HeapByteBuffer[pos=0 lim=10 cap=10]
--------------------------------------
put完之後-->limit--->10
put完之後-->position--->4
put完之後-->capacity--->10
put完之後-->mark--->java.nio.HeapByteBuffer[pos=4 lim=10 cap=10]
flip方法
現在我想要從緩存區拿數據,怎麼拿呢?NIO給了我們一個flip()方法。這個方法可以改動position和limit的位置!
byteBuffer.flip();
System.out.println("flip完之後-->limit--->" + byteBuffer.limit());
System.out.println("flip完之後-->position--->" + byteBuffer.position());
System.out.println("flip完之後-->capacity--->" + byteBuffer.capacity());
System.out.println("flip完之後-->mark--->" + byteBuffer.mark());
flip完之後-->limit--->4
flip完之後-->position--->0
flip完之後-->capacity--->10
flip完之後-->mark--->java.nio.HeapByteBuffer[pos=0 lim=4 cap=10]
一般我們稱filp()爲“切換成讀模式”
get方法
get方法讀取信息position也會對應的移動!
// 一個字節一個字節的讀取
System.out.println((char)byteBuffer.get());
System.out.println((char)byteBuffer.get());
System.out.println((char)byteBuffer.get());
System.out.println((char)byteBuffer.get());
System.out.println("get完之後-->mark--->" + byteBuffer.mark());
// get方法 讀取了多少個字節,position就會移動多少位,相應再次重新讀取需要flip
byteBuffer.flip();
byte[] b = new byte[byteBuffer.limit()];
// 批量讀取數據
byteBuffer.get(b);
System.out.println(new String(b,0,b.length));
System.out.println("get完之後-->mark--->" + byteBuffer.mark());
輸出
b
o
b
o
get完之後-->mark--->java.nio.HeapByteBuffer[pos=4 lim=4 cap=10]
bobo
clear方法
數據操作完成後我們還想要繼續寫入數據,這時我們可以使用clear方法來’清空’緩衝區。數據沒有真正被清空,只是被遺忘掉了
hasRemaining()
檢查position和limit之間是否還有元素。判斷是否還有剩餘元素
// 一個字節一個字節的讀取
System.out.println((char)byteBuffer.get());
System.out.println("hasRemaining:"+byteBuffer.hasRemaining());
System.out.println((char)byteBuffer.get());
System.out.println("hasRemaining:"+byteBuffer.hasRemaining());
System.out.println((char)byteBuffer.get());
System.out.println("hasRemaining:"+byteBuffer.hasRemaining());
System.out.println((char)byteBuffer.get());
System.out.println("hasRemaining:"+byteBuffer.hasRemaining());
輸出
b
hasRemaining:true
o
hasRemaining:true
b
hasRemaining:true
o
hasRemaining:false
rewind方法
讀完一遍數據後,我們還想再讀取一遍,此時可以考慮rewind方法
注意他和flip方法的區別
通過源碼分析會更加清晰些~
---------------------
作者:波波烤鴨
來源:CSDN
原文:https://dpb-bobokaoya-sm.blog.csdn.net/article/details/89176469
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!