1. 程式人生 > >Java NIO入門學習(二)

Java NIO入門學習(二)

在上一篇中,我們介紹了NIO中的兩個核心物件:緩衝區和通道。本文為NIO入門學習的第二篇,將會分析NIO中的緩衝區Buffer的內部原理。

在談到緩衝區時,我們說緩衝區物件本質上是一個數組,但它其實是一個特殊的陣列,緩衝區物件內建了一些機制,能夠跟蹤和記錄緩衝區的狀態變化情況,如果我們使用get()方法從緩衝區獲取資料或者使用put()方法把資料寫入緩衝區,都會引起緩衝區狀態的變化。

在緩衝區中,最重要的屬性有下面三個,它們一起合作完成對緩衝區內部狀態的變化跟蹤:

position

指定了下一個將要被寫入或者讀取的元素索引。在從Channel讀取資料到Buffer時,position變數用來跟蹤截止目前為止從Channel中讀出了多少資料,在從Buffer中向Channel寫資料時,position變數用來跟蹤截止目前為止向Channel寫入了多少資料。

limit

在從Channel中讀取資料到Buffer中時,limit變數指示了還剩多少空間可供存放資料,在從Buffer向Channel寫資料時,limit變數指示了還剩多少資料可以寫入。position正常情況下小於或者等於limit。

capacity

指示Buffer最多能夠儲存的資料。實際上,它指示了底層array的容量,或者至少是底層array允許使用的空間數量。Limit永遠不會大於capacity。

接下來我們將逐一檢查每個細節,並且也看看為什麼這樣的設計適合典型的讀/寫(輸入/輸出)處理。我們假設從一個Channel拷貝資料到另一個Channel。

首先新建一個容量大小為10的ByteBuffer物件,在初始化的時候,position設定為0,如果我們讀一些資料到緩衝區中,那麼下一個讀取的資料就進入索引為0的位元組

。如果我們從緩衝區寫一些資料,從緩衝區讀取的下一個位元組就來自索引為0的位元組。limit和 capacity被設定為10,在以後使用ByteBuffer物件過程中,capacity的值不會再發生變化,而其它兩個將會隨著使用而變化。


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


下一步把讀取到的資料寫入到寫通道中,在此之前必須呼叫flip()方法,該方法將會完成兩件事情:

1. 把limit設定為當前的position值

2. 把position設定為0

position

 被設定為 0,這意味著我們得到的下一個位元組是第一個位元組。limit 已被設定為原來的 position,這意味著它包括以前讀到的所有位元組,並且一個位元組也不多,如下圖所示:


我們現在可以將資料從緩衝區寫入通道了,這會導致position的增加而limit保持不變,但position不會超過limit的值,所以在讀取我們之前寫入到緩衝區中的4個位元組之後,position和limit的值都為4,如下圖所示:


在資料寫入到寫通道完畢後,呼叫clear()方法能夠把所有的狀態變化設定為初始化時的值,該方法將會完成兩件事情:

1. 把limit設定為capacity值

2. 把position設定為0

clear()方法會重置Buffer以便接收更多的位元組,如下圖所示:


最後我們用一段程式碼來驗證這個過程,如下所示:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class TestBufferField {

	public static void main(String[] args) throws Exception {
		FileInputStream fileInStream = new FileInputStream("D:\\test1.txt");
		// 獲取讀通道
		FileChannel fcin = fileInStream.getChannel();

		FileOutputStream fileOutStream = new FileOutputStream("D:\\test2.txt");
		// 獲取寫通道
		FileChannel fcout = fileOutStream.getChannel();

		// 建立緩衝區
		ByteBuffer buffer = ByteBuffer.allocate(10);
		output("初始化", buffer);

		// 從通道fcin讀取資料到緩衝區
		fcin.read(buffer);
		output("呼叫read()", buffer);

		// 重設buffer,將limit設定為position,然後將position設定為0
		buffer.flip();
		output("呼叫flip()", buffer);

		// 將緩衝區中的資料寫入通道fcout
		fcout.write(buffer);
		output("呼叫write()", buffer);

		// 重設buffer,將limit設定為容量capacity,position設定為0
		buffer.clear();
		output("呼叫clear()", buffer);

		fileInStream.close();
		fileOutStream.close();
	}

	public static void output(String step, Buffer buffer) {
		System.out.println(step + " : ");
		System.out.print("position: " + buffer.position() + ", ");
		System.out.print("limit: " + buffer.limit() + ", ");
		System.out.println("capacity: " + buffer.capacity());
		System.out.println();
	}

}

輸出結果為:


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

參考:

NIO 入門