天天看點

Java NIO 詳解(一)

i/o即輸入輸出,是計算機與外界世界的一個借口。io操作的實際主題是作業系統。在java程式設計中,一般使用流的方式來處理io,所有的io都被視作是單個位元組的移動,通過stream對象一次移動一個位元組。流io負責把對象轉換為位元組,然後再轉換為對象。

nio即new io,這個庫是在jdk1.4中才引入的。nio和io有相同的作用和目的,但實作方式不同,nio主要用到的是塊,是以nio的效率要比io高很多。

nio和io最大的差別是資料打包和傳輸方式。io是以流的方式處理資料,而nio是以塊的方式處理資料。

面向流的io一次一個位元組的處理資料,一個輸入流産生一個位元組,一個輸出流就消費一個位元組。為流式資料建立過濾器就變得非常容易,連結幾個過濾器,以便對資料進行處理非常友善而簡單,但是面向流的io通常處理的很慢。

面向塊的io系統以塊的形式處理資料。每一個操作都在一步中産生或消費一個資料塊。按塊要比按流快的多,但面向塊的io缺少了面向流io所具有的有雅興和簡單性。

channel是對原io中流的模拟,任何來源和目的資料都必須通過一個channel對象。一個buffer實質上是一個容器對象,發給channel的所有對象都必須先放到buffer中;同樣的,從channel中讀取的任何資料都要讀到buffer中。

buffer是一個對象,它包含一些要寫入或讀出的資料。在nio中,資料是放入buffer對象的,而在io中,資料是直接寫入或者讀到stream對象的。應用程式不能直接對 channel 進行讀寫操作,而必須通過 buffer 來進行,即 channel 是通過 buffer 來讀寫資料的。

在nio中,所有的資料都是用buffer處理的,它是nio讀寫資料的中轉池。buffer實質上是一個數組,通常是一個位元組資料,但也可以是其他類型的數組。但一個緩沖區不僅僅是一個數組,重要的是它提供了對資料的結構化通路,而且還可以跟蹤系統的讀寫程序。

使用 buffer 讀寫資料一般遵循以下四個步驟:

寫入資料到 buffer;

調用 flip() 方法;

從 buffer 中讀取資料;

調用 clear() 方法或者 compact() 方法。

當向 buffer 寫入資料時,buffer 會記錄下寫了多少資料。一旦要讀取資料,需要通過 flip() 方法将 buffer 從寫模式切換到讀模式。在讀模式下,可以讀取之前寫入到 buffer 的所有資料。

一旦讀完了所有的資料,就需要清空緩沖區,讓它可以再次被寫入。有兩種方式能清空緩沖區:調用 clear() 或 compact() 方法。clear() 方法會清空整個緩沖區。compact() 方法隻會清除已經讀過的資料。任何未讀的資料都被移到緩沖區的起始處,新寫入的資料将放到緩沖區未讀資料的後面。

buffer主要有如下幾種:

Java NIO 詳解(一)

channel是一個對象,可以通過它讀取和寫入資料。可以把它看做io中的流。但是它和流相比還有一些不同:

channel是雙向的,既可以讀又可以寫,而流是單向的

channel可以進行異步的讀寫

對channel的讀寫必須通過buffer對象

正如上面提到的,所有資料都通過buffer對象處理,是以,您永遠不會将位元組直接寫入到channel中,相反,您是将資料寫入到buffer中;同樣,您也不會從channel中讀取位元組,而是将資料從channel讀入buffer,再從buffer擷取這個位元組。

因為channel是雙向的,是以channel可以比流更好地反映出底層作業系統的真實情況。特别是在unix模型中,底層作業系統通常都是雙向的。

Java NIO 詳解(一)

在java nio中channel主要有如下幾種類型:

filechannel:從檔案讀取資料的

datagramchannel:讀寫udp網絡協定資料

socketchannel:讀寫tcp網絡協定資料

serversocketchannel:可以監聽tcp連接配接

io中的讀和寫,對應的是資料和stream,nio中的讀和寫,則對應的就是通道和緩沖區。nio中從通道中讀取:建立一個緩沖區,然後讓通道讀取資料到緩沖區。nio寫入資料到通道:建立一個緩沖區,用資料填充它,然後讓通道用這些資料來執行寫入。

我們已經知道,在nio系統中,任何時候執行一個讀操作,您都是從channel中讀取,而您不是直接從channel中讀取資料,因為所有的資料都必須用buffer來封裝,是以您應該是從channel讀取資料到buffer。

是以,如果從檔案讀取資料的話,需要如下三步:

從fileinputstream擷取channel

建立buffer

從channel讀取資料到buffer

下面我們看一下具體過程:

第一步:擷取通道

第二步:建立緩沖區

第三步:将資料從通道讀到緩沖區

類似于從檔案讀資料,

第一步:擷取一個通道

第二步:建立緩沖區,将資料放入緩沖區

第三步:把緩沖區資料寫入通道中

copyfile是一個非常好的讀寫結合的例子,我們将通過copyfile這個實力讓大家體會nio的操作過程。copyfile執行三個基本的操作:建立一個buffer,然後從源檔案讀取資料到緩沖區,然後再将緩沖區寫入目标檔案。

上面程式中有三個地方需要注意

當沒有更多的資料時,拷貝就算完成,此時 read() 方法會傳回 -1 ,我們可以根據這個方法判斷是否讀完。

position:跟蹤已經寫了多少資料或讀了多少資料,它指向的是下一個位元組來自哪個位置

limit:代表還有多少資料可以取出或還有多少空間可以寫入,它的值小于等于capacity。

capacity:代表緩沖區的最大容量,一般建立一個緩沖區的時候,limit的值和capacity的值預設是相等的。

flip、clear這兩個方法便是用來設定這些值的。

我們先看一下flip的源碼:

Java NIO 詳解(一)

在上面的filecopy程式中,寫入資料之前我們調用了<code>buffer.flip();</code>方法,這個方法把目前的指針位置position設定成了limit,再将目前指針position指向資料的最開始端,我們現在可以将資料從緩沖區寫入通道了。 position 被設定為 0,這意味着我們得到的下一個位元組是第一個位元組。 limit 已被設定為原來的 position,這意味着它包括以前讀到的所有位元組,并且一個位元組也不多。

先看一下clear的源碼:

Java NIO 詳解(一)

在上面的filecopy程式中,寫入資料之後也就是讀資料之前,我們調用了 <code>buffer.clear();</code>方法,這個方法重設緩沖區以便接收更多的位元組。上圖顯示了在調用 clear() 後緩沖區的狀态。

下一篇: Java IO