◆ 輸入/輸出:概念性描述
I/O 簡介
I/O ? 或者輸入/輸出 ? 指的是計算機與外部世界或者一個程序與計算機的其余部分的之間的接口。它對于任何計算機系統都非常關鍵,因而所有 I/O 的主體實際上是內置在操作系統中的。單獨的程序一般是讓系統為它們完成大部分的工作。
在 Java 編程中,直到最近一直使用 流 的方式完成 I/O。所有 I/O 都被視為單個的字節的移動,通過一個稱為 Stream 的對象一次移動一個字節。流 I/O 用于與外部世界接觸。它也在內部使用,用于將對象轉換為字節,然后再轉換回對象。
NIO 與原來的 I/O 有同樣的作用和目的,但是它使用不同的方式? 塊 I/O。正如您將在本教程中學到的,塊 I/O 的效率可以比流 I/O 高許多。
為什么要使用 NIO?
NIO 的創建目的是為了讓 Java 程序員可以實現高速 I/O 而無需編寫自定義的本機代碼。NIO 將最耗時的 I/O 操作(即填充和提取緩沖區)轉移回操作系統,因而可以極大地提高速度。
流與塊的比較
原來的 I/O 庫(在 java.io.*中) 與 NIO 最重要的區別是數據打包和傳輸的方式。正如前面提到的,原來的 I/O 以流的方式處理數據,而 NIO 以塊的方式處理數據。
面向流 的 I/O 系統一次一個字節地處理數據。一個輸入流產生一個字節的數據,一個輸出流消費一個字節的數據。為流式數據創建過濾器非常容易。鏈接幾個過濾器,以便每個過濾器只負責單個復雜處理機制的一部分,這樣也是相對簡單的。不利的一面是,面向流的 I/O 通常相當慢。
一個 面向塊 的 I/O 系統以塊的形式處理數據。每一個操作都在一步中產生或者消費一個數據塊。按塊處理數據比按(流式的)字節處理數據要快得多。但是面向塊的 I/O 缺少一些面向流的 I/O 所具有的優雅性和簡單性。
集成的 I/O
在 JDK 1.4 中原來的 I/O 包和 NIO 已經很好地集成了。 java.io.* 已經以 NIO 為基礎重新實現了,所以現在它可以利用 NIO 的一些特性。例如, java.io.* 包中的一些類包含以塊的形式讀寫數據的方法,這使得即使在更面向流的系統中,處理速度也會更快。
也可以用 NIO 庫實現標準 I/O 功能。例如,可以容易地使用塊 I/O 一次一個字節地移動數據。但是正如您會看到的,NIO 還提供了原 I/O 包中所沒有的許多好處。
◆ 通道和緩沖區
概 述
通道 和 緩沖區 是 NIO 中的核心對象,幾乎在每一個 I/O 操作中都要使用它們。
通道是對原 I/O 包中的流的模擬。到任何目的地(或來自任何地方)的所有數據都必須通過一個 Channel 對象。一個 Buffer 實質上是一個容器對象。發送給一個通道的所有對象都必須首先放到緩沖區中;同樣地,從通道中讀取的任何數據都要讀到緩沖區中。
在本節中,您會了解到 NIO 中通道和緩沖區是如何工作的。
什么是緩沖區?
Buffer 是一個對象, 它包含一些要寫入或者剛讀出的數據。 在 NIO 中加入 Buffer 對象,體現了新庫與原 I/O 的一個重要區別。在面向流的 I/O 中,您將數據直接寫入或者將數據直接讀到 Stream 對象中。
在 NIO 庫中,所有數據都是用緩沖區處理的。在讀取數據時,它是直接讀到緩沖區中的。在寫入數據時,它是寫入到緩沖區中的。任何時候訪問 NIO 中的數據,您都是將它放到緩沖區中。
緩沖區實質上是一個數組。通常它是一個字節數組,但是也可以使用其他種類的數組。但是一個緩沖區不 僅僅 是一個數組。緩沖區提供了對數據的結構化訪問,而且還可以跟蹤系統的讀/寫進程。
緩沖區類型
最常用的緩沖區類型是 ByteBuffer。一個 ByteBuffer 可以在其底層字節數組上進行 get/set 操作(即字節的獲取和設置)。
ByteBuffer 不是 NIO 中唯一的緩沖區類型。事實上,對于每一種基本 Java 類型都有一種緩沖區類型:
• ByteBuffer
• CharBuffer
• ShortBuffer
• IntBuffer
• LongBuffer
• FloatBuffer
• DoubleBuffer
每一個 Buffer 類都是 Buffer 接口的一個實例。 除了 ByteBuffer,每一個 Buffer 類都有完全一樣的操作,只是它們所處理的數據類型不一樣。因為大多數標準 I/O 操作都使用 ByteBuffer,所以它具有所有共享的緩沖區操作以及一些特有的操作。
現在您可以花一點時間運行 UseFloatBuffer.java,它包含了類型化的緩沖區的一個應用例子。
什么是通道?
Channel是一個對象,可以通過它讀取和寫入數據。拿 NIO 與原來的 I/O 做個比較,通道就像是流。
正如前面提到的,所有數據都通過 Buffer 對象來處理。您永遠不會將字節直接寫入通道中,相反,您是將數據寫入包含一個或者多個字節的緩沖區。同樣,您不會直接從通道中讀取字節,而是將數據從通道讀入緩沖區,再從緩沖區獲取這個字節。
通道類型
通道與流的不同之處在于通道是雙向的。而流只是在一個方向上移動(一個流必須是 InputStream 或者 OutputStream 的子類), 而 通道 可以用于讀、寫或者同時用于讀寫。
因為它們是雙向的,所以通道可以比流更好地反映底層操作系統的真實情況。特別是在 UNIX 模型中,底層操作系統通道是雙向的。
◆ 從理論到實踐:NIO 中的讀和寫
概 述
讀和寫是 I/O 的基本過程。從一個通道中讀取很簡單:只需創建一個緩沖區,然后讓通道將數據讀到這個緩沖區中。寫入也相當簡單:創建一個緩沖區,用數據填充它,然后讓通道用這些數據來執行寫入操作。
在本節中,我們將學習有關在 Java 程序中讀取和寫入數據的一些知識。我們將回顧 NIO 的主要組件(緩沖區、通道和一些相關的方法),看看它們是如何交互以進行讀寫的。在接下來的幾節中,我們將更詳細地分析這其中的每個組件以及其交互。
從文件中讀取
在我們第一個練習中,我們將從一個文件中讀取一些數據。如果使用原來的 I/O,那么我們只需創建一個 FileInputStream 并從它那里讀取。而在 NIO 中,情況稍有不同:我們首先從 FileInputStream 獲取一個 FileInputStream 對象,然后使用這個通道來讀取數據。
在 NIO 系統中,任何時候執行一個讀操作,您都是從通道中讀取,但是您不是 直接 從通道讀取。因為所有數據最終都駐留在緩沖區中,所以您是從通道讀到緩沖區中。
因此讀取文件涉及三個步驟:(1) 從 FileInputStream 獲取 Channel,(2) 創建 Buffer,(3) 將數據從 Channel 讀到 Buffer 中。
現在,讓我們看一下這個過程。
三個容易的步驟
第一步是獲取通道。我們從 FileInputStream 獲取通道:
FileInputStream fin = new FileInputStream( "readandshow.txt" );
FileChannel fc = fin.getChannel();
下一步是創建緩沖區:
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
最后,需要將數據從通道讀到緩沖區中,如下所示:
fc.read( buffer );
您會注意到,我們不需要告訴通道要讀 多少數據 到緩沖區中。每一個緩沖區都有復雜的內部統計機制,它會跟蹤已經讀了多少數據以及還有多少空間可以容納更多的數據
寫入文件
在 NIO 中寫入文件類似于從文件中讀取。首先從 FileOutputStream 獲取一個通道:
FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
FileChannel fc = fout.getChannel();
下一步是創建一個緩沖區并在其中放入一些數據 - 在這里,數據將從一個名為 message 的數組中取出,這個數組包含字符串 "Some bytes" 的 ASCII 字節(本教程后面將會解釋 buffer.flip() 和 buffer.put() 調用)。
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
for (int i=0; i
buffer.put( message[i] );
}
buffer.flip();
最后一步是寫入緩沖區中
fc.write( buffer );
注意在這里同樣不需要告訴通道要寫入多數據。緩沖區的內部統計機制會跟蹤它包含多少數據以及還有多少數據要寫入。
讀寫結合
下面我們將看一下在結合讀和寫時會有什么情況。我們以一個名為 CopyFile.java 的簡單程序作為這個練習的基礎,它將一個文件的所有內容拷貝到另一個文件中。CopyFile.java 執行三個基本操作:首先創建一個 Buffer,然后從源文件中將數據讀到這個緩沖區中,然后將緩沖區寫入目標文件。這個程序不斷重復
主站蜘蛛池模板:
南漳县|
伊通|
博爱县|
图们市|
老河口市|
虞城县|
进贤县|
西峡县|
枣阳市|
福鼎市|
沂源县|
荣成市|
东兰县|
肃北|
晋宁县|
安顺市|
温泉县|
禹州市|
秦安县|
常德市|
文安县|
兴义市|
盐津县|
乐平市|
南开区|
萍乡市|
长岛县|
剑阁县|
黄龙县|
色达县|
江北区|
鲁山县|
安图县|
玉溪市|
乌兰浩特市|
永嘉县|
洪江市|
云浮市|
麟游县|
临夏市|
马尔康县|