Buffer之position,limit,capacity
一、前言
熟悉NIO的人想必一定不會陌生buffer中position,limit,capacity這三個屬性吧,之前在學習的時候遇到一個問題:就是當你先往緩衝區寫入一部分資料,然後呼叫flip()方法,再全部讀取完資料,然後再呼叫flip()方法,此時這三個值的變化是怎樣的,研究了一下,決定寫下來分享一下。
二、正文
1、介紹
position : 它指的是下一次讀取或寫入的位置。
limit : 指定還有多少資料需要寫出(在從緩衝區寫入通道時),或者還有多少空間可以讀入資料(在從通道讀入緩衝區時),它初始化是與capacity的值一樣,當呼叫flip()方法之後,它的值會改變成position的值,而position被置0。它箭頭所指的位置是最後一位元素的下一位所在的位置*
capacity : 指定了可以儲存在緩衝區中的最大資料容量,實際上,它指定了底層陣列的大小,或者至少是指定了准許我們使用的底層陣列的容量,這個初始化後就不會再改變了。
2、圖示
以上三個屬性值之間有一些相對大小的關係:0 <= position <= limit <= capacity。如果我們建立一個新的容量大小為7的ByteBuffer物件,在初始化的時候,position設定為0,limit和 capacity被設定為7,在以後使用ByteBuffer物件過程中,capacity的值不會再發生變化,而其它兩個個將會隨著使用而變化。三個屬性值分別如圖所示:
初始化:

假設我們現在要往這個緩衝區裡面寫入3個位元組,寫完之後,position的箭頭就會指向3的位置,而limit不變:

此時我們想從緩衝區讀取這3個位元組,就必須呼叫flip()方法,呼叫了flip()方法過後,limit置為position的位置,而position被置為0,也正應證了上面所說的,position它指的是下一次讀取或寫入的位置,limit它箭頭所指的位置是最後一位元素的下一位所在的位置:

現在我們可以呼叫get()方法,一直從緩衝區裡面取資料,直到取完為止,也就是當position與limit的值一樣時,就取完了:

這一次簡單的讀寫操作就完成了,如果想恢復成初始狀態的話,可以呼叫clear()方法:

之前學到這裡的時候有個疑問,不知道大家想過沒有,就是我們在呼叫了get()方法從緩衝區取完裡面的資料,立馬去呼叫flip()方法,那這三個屬性的值會是什麼變化?如果當我只讀了2個位元組的資料之後,就不讀了,然後再去呼叫flip(),這三個值又會是怎麼變化?其實不管怎麼繞,你只要懂得原理,就不難,咱們先看flip()原始碼做了什麼:
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
這裡不難發現,呼叫flip()方法,無非就是給這幾個變數賦值,將當前的position值賦給limit,然後將position的值置為0,Mark是一個標誌變數,咱們以後會提到。熟悉以上程式碼就不難解決我提出的2個問題:
當你讀取完呼叫flip()的方法 positon:0 limit:3 capacity:7
當你讀取2個位元組之後呼叫flip()方法 positon:0 limit:2 capacity:7
這裡就解決了我之前遇到的這三個屬性值變化的問題!!!
三、測試程式碼
讀取完呼叫flip:
package com.cing.nio;
import java.io.FileInputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NioTest1 {
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream("D:\\A.txt");
FileChannel fc = fis.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(7);
output("初始化", buffer);
fc.read(buffer);
output("呼叫READ方法", buffer);
buffer.flip();
output("第一次呼叫flip", buffer);
while (buffer.remaining() > 0) {
byte b = buffer.get();
}
output("get()", buffer);
buffer.flip();
output("第二次flip", buffer);
fis.close();
}
public static void output(String step, Buffer buffer) {
System.out.println(step + " : ");
System.out.println("buffer: " + buffer + ", ");
}
}
輸出結果為:
初始化 :
buffer: java.nio.HeapByteBuffer[pos=0 lim=7 cap=7],
呼叫READ方法 :
buffer: java.nio.HeapByteBuffer[pos=3 lim=7 cap=7],
第一次呼叫flip :
buffer: java.nio.HeapByteBuffer[pos=0 lim=3 cap=7],
get() :
buffer: java.nio.HeapByteBuffer[pos=3 lim=3 cap=7],
第二次flip :
buffer: java.nio.HeapByteBuffer[pos=0 lim=3 cap=7],
讀取2位元組之後呼叫flip:
package com.cing.nio; import java.io.FileInputStream; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class NioTest1 { public static void main(String[] args) throws Exception{ FileInputStream fis = new FileInputStream("D:\\A.txt"); FileChannel fc = fis.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(7); output("初始化", buffer); fc.read(buffer); output("呼叫READ方法", buffer); buffer.flip(); output("第一次呼叫flip", buffer); while (buffer.remaining() > 1) { byte b = buffer.get(); } output("get()", buffer); buffer.flip(); output("第二次flip", buffer); fis.close(); } public static void output(String step, Buffer buffer) { System.out.println(step + " : "); System.out.println("buffer: " + buffer + ", "); } }
輸出結果為:
初始化 :
buffer: java.nio.HeapByteBuffer[pos=0 lim=7 cap=7],
呼叫READ方法 :
buffer: java.nio.HeapByteBuffer[pos=3 lim=7 cap=7],
第一次呼叫flip :
buffer: java.nio.HeapByteBuffer[pos=0 lim=3 cap=7],
get() :
buffer: java.nio.HeapByteBuffer[pos=2 lim=3 cap=7],
第二次flip :
buffer: java.nio.HeapByteBuffer[pos=0 lim=2 cap=7],
NIO的知識學起來還是比較有趣的,期待下次與大家分享這塊的知識!