緩沖區分類
NIO中的buffer用於和通道交互,數據是從通道讀入緩沖區,從緩沖區中寫入通道的。Buffer就像一個數組,可以保存多個類型相同的數據。每種基本數據類型都有對應的Buffer類:
緩沖區的屬性
1、capacity(容量):buffer本質是一個數組,在初始化時有固定的大小,這個值就是容量。容量不可改變,一旦緩沖區滿了,需要將其清空才能將繼續進行讀寫操作。
2、position(位置):表示當前的位置,初始化時為0,當一個基本數據類型的數據寫入buffer時,position會向前移動到下一個可插入數據的Buffer單元。position最大值可以是 capacity-1。
3、limit(限制):在緩沖區寫模式下,limit表示你最多能往Buffer里寫多少數據,大小等於capacity;在緩沖區讀模式下,limit表示能從緩沖區內讀取到多少數據,因此,當切 換Buffer到讀模式時,limit會被設置成寫模式下的position值。
一、使用NIO進行文件內容的復制:
public class BufferTest { public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("d:/in.txt"); FileChannel channel = fis.getChannel(); FileOutputStream fos = new FileOutputStream("d:/out.txt"); FileChannel channel1 = fos.getChannel(); //初始化緩沖區 ByteBuffer buffer = ByteBuffer.allocate(20); System.out.println("通道文件的大小:" + channel.size()); System.out.println("緩沖區初始化時當前位置:" + buffer.position()); System.out.println("緩沖區初始化時可寫的限制:" + buffer.limit()); System.out.println("---------循環開始-----"); //判斷通道內數據是否讀取完成 while(-1 != channel.read(buffer)) { System.out.println("緩沖區寫模式下當前位置:" + buffer.position()); System.out.println("緩沖區寫模式下的限制:" + buffer.limit()); //將緩沖區從寫模式切換到讀模式 buffer.flip(); System.out.println("緩沖區讀模式下當前位置:" + buffer.position()); System.out.println("緩沖區讀模式下的限制:" + buffer.limit()); //判斷緩沖區內是否還有數據可讀取 while(buffer.hasRemaining()) { channel1.write(buffer); } buffer.clear(); } channel.close(); channel1.close(); fis.close(); } }
執行結果:
通道文件的大小:36 緩沖區初始化時當前位置:0 緩沖區初始化時可寫的限制:20 ---------循環開始----- 緩沖區寫模式下當前位置:20 緩沖區寫模式下的限制:20 緩沖區讀模式下當前位置:0 緩沖區讀模式下的限制:20 緩沖區寫模式下當前位置:16 緩沖區寫模式下的限制:20 緩沖區讀模式下當前位置:0 緩沖區讀模式下的限制:16
1、文件的大小為36個字節,緩沖區初始化的大小為20個字節,程序中進行了兩次讀取操作,才完成了文件內容的復制。
2、可以看到,在緩沖區寫模式下,limit的大小始終等於capacity;而在讀模式下,limit等於模式切換前position的大小。
二、Buffer的分配
Buffer對象的獲取需要進行分配,每種類型的Buffer對象都有一個allocate方法。我們以程序中的ByteBuffer對象為例:
ByteBuffer buffer = ByteBuffer.allocate(20);
我們去跟蹤下源碼:
1 public static ByteBuffer allocate(int capacity) { 2 if (capacity < 0) 3 throw new IllegalArgumentException(); 4 return new HeapByteBuffer(capacity, capacity); 5 }
HeapByteBuffer(int cap, int lim) { // package-private super(-1, 0, lim, cap, new byte[cap], 0);
//在這里已經創建一個以cap為大小的字節數組(new byte[cap])
ByteBuffer(int mark, int pos, int lim, int cap, // package-private byte[] hb, int offset) { super(mark, pos, lim, cap); this.hb = hb; this.offset = offset; }
Buffer(int mark, int pos, int lim, int cap) { // package-private if (cap < 0) throw new IllegalArgumentException(); this.capacity = cap; limit(lim); position(pos); if (mark >= 0) { if (mark > pos) throw new IllegalArgumentException(); this.mark = mark; } }
//數組的創建在ByteBuffer類里面已經創建,在父類Buffer里,初始化容量、限制、位置等一些公共屬性。
三、Buffer模式的切換
buffer.flip()該方法是用於將緩沖區從寫模式切換到讀模式,這是一種固定寫法,該方法的源碼如下:
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
調用flip()方法會將position設回0,並將limit設置成之前position的值。
四、remaind方法
public final Buffer rewind() { position = 0; mark = -1; return this; }
將position的位置設置為0,表示可以重新讀取Buffer中的所有數據,limit保持不變。
五、clear方法
1 public final Buffer clear() { 2 position = 0; 3 limit = capacity; 4 mark = -1; 5 return this;
1、一旦完成對buffer中數據的讀取,需要讓buffer做好再次被寫入的准備,這時候可以調用clear方法來完成。
2、clear方法將position設置為0,limit設置為容量的值,也就意味着buffer被清空了,但是這個清空的概念是寫入數據可以從緩沖區的指定位置開始,但buffer里面的數據並沒有 刪除。
3、如果buffer里面還有數據沒有被讀取,這個時候調用clear方法會導致那些數據被“遺忘”,因為沒有標記告訴你哪些是讀取過哪些沒有被讀取。
六、向buffer中寫入數據
1、通過channel寫入;
2、通過buffer的put方法寫入:
buffer.put("channel".getBytes());
七、從buffer中讀取數據
1、通過channel讀取;
2、通過buffer的get方法讀取:
byte b = buffer.get();