Java NIO使用及原理分析(二)
- 2019 年 10 月 7 日
- 筆記
在第一篇中,我們介紹了NIO中的兩個核心對象:緩衝區和通道,在談到緩衝區時,我們說緩衝區對象本質上是一個數組,但它其實是一個特殊的數組,緩衝區對象內置了一些機制,能夠跟蹤和記錄緩衝區的狀態變化情況,如果我們使用get()方法從緩衝區獲取數據或者使用put()方法把數據寫入緩衝區,都會引起緩衝區狀態的變化。本文為NIO使用及原理分析的第二篇,將會分析NIO中的Buffer對象。
在緩衝區中,最重要的屬性有下面三個,它們一起合作完成對緩衝區內部狀態的變化跟蹤:
position:指定了下一個將要被寫入或者讀取的元素索引,它的值由get()/put()方法自動更新,在新創建一個Buffer對象時,position被初始化為0。
limit:指定還有多少數據需要取出(在從緩衝區寫入通道時),或者還有多少空間可以放入數據(在從通道讀入緩衝區時)。
capacity:指定了可以存儲在緩衝區中的最大數據容量,實際上,它指定了底層數組的大小,或者至少是指定了准許我們使用的底層數組的容量。
以上四個屬性值之間有一些相對大小的關係:0 <= position <= limit <= capacity。如果我們創建一個新的容量大小為10的ByteBuffer對象,在初始化的時候,position設置為0,limit和 capacity被設置為10,在以後使用ByteBuffer對象過程中,capacity的值不會再發生變化,而其它兩個個將會隨着使用而變化。四個屬性值分別如圖所示:

現在我們可以從通道中讀取一些數據到緩衝區中,注意從通道讀取數據,相當於往緩衝區中寫入數據。如果讀取4個自己的數據,則此時position的值為4,即下一個將要被寫入的位元組索引為4,而limit仍然是10,如下圖所示:

下一步把讀取的數據寫入到輸出通道中,相當於從緩衝區中讀取數據,在此之前,必須調用flip()方法,該方法將會完成兩件事情:
1. 把limit設置為當前的position值 2. 把position設置為0
由於position被設置為0,所以可以保證在下一步輸出時讀取到的是緩衝區中的第一個位元組,而limit被設置為當前的position,可以保證讀取的數據正好是之前寫入到緩衝區中的數據,如下圖所示:

現在調用get()方法從緩衝區中讀取數據寫入到輸出通道,這會導致position的增加而limit保持不變,但position不會超過limit的值,所以在讀取我們之前寫入到緩衝區中的4個自己之後,position和limit的值都為4,如下圖所示:

在從緩衝區中讀取數據完畢後,limit的值仍然保持在我們調用flip()方法時的值,調用clear()方法能夠把所有的狀態變化設置為初始化時的值,如下圖所示:

最後我們用一段代碼來驗證這個過程,如下所示:
[java] view plain copy print?
- import java.io.*;
- import java.nio.*;
- import java.nio.channels.*;
- public class Program {
- public static void main(String args[]) throws Exception {
- FileInputStream fin = new FileInputStream("d:\test.txt");
- FileChannel fc = fin.getChannel();
- ByteBuffer buffer = ByteBuffer.allocate(10);
- output("初始化", buffer);
- fc.read(buffer);
- output("調用read()", buffer);
- buffer.flip();
- output("調用flip()", buffer);
- while (buffer.remaining() > 0) {
- byte b = buffer.get();
- // System.out.print(((char)b));
- }
- output("調用get()", buffer);
- buffer.clear();
- output("調用clear()", buffer);
- fin.close();
- }
- public static void output(String step, Buffer buffer) {
- System.out.println(step + " : ");
- System.out.print("capacity: " + buffer.capacity() + ", ");
- System.out.print("position: " + buffer.position() + ", ");
- System.out.println("limit: " + buffer.limit());
- System.out.println();
- }
- }
完成的輸出結果為:

這與我們上面演示的過程一致。在後面的文章中,我們繼續介紹NIO中關於緩衝區一些更高級的使用。