標籤:
Java NIOI/O簡介
I/O或者輸入輸出指的是電腦與外部世界或者一個程式與電腦的其餘部分之間的介面。它對於任何電腦系統都非常關鍵,因而所有I/O的主體實際上是內建在作業系統中的。單獨的程式一般是讓系統為它們完成大部分的工作。
在Java編程中,直到最近一直使用流的方式完成I/O。所有I/O都被視為單個的位元組的移動,通過一個稱為Stream的對象一次移動一個位元組。流I/O用於與外部世界接觸。它也在內部使用,用於將對象轉換為位元組,然後再轉換回對象。
NIO與原來的I/O有同樣的作用和目的,但是它使用不同的方式:塊I/O,其效率要比流I/O高許多。
為什麼要用NIO?
NIO的建立目的是為了讓java程式員可以實現高速I/O而無需編寫自訂的機器碼。NIO將最耗時的I/O操作(即填充和提取緩衝區)轉移回作業系統,因而可以極大的提高速度。
流與塊的比較
原來的I/O庫與NIO最重要的區別是資料打包和傳輸方式。正如前面提到的,原來的I/O以流的方式處理資料,而NIO以塊的方式處理資料。
面向流的I/O
系統一次一個位元組地處理資料,一個輸入資料流產生一個位元組的資料,一個輸出資料流消費一個位元組的資料。為流式資料建立過濾器非常容易。連結幾個過濾器,以便每個過濾器只負責單個複雜處理機制的一部分。但不利的一面是,面向流I/O通常相當慢。
面向塊的I/O
系統以塊的形式處理資料。每一個操作都在一步中產生或者消費一個資料區塊,按塊處理資料的方式要比按串流資料的方式要快的多。但相對來說,沒有面向流I/O簡單。
通道與緩衝區
通道與緩衝區是NIO中的核心對象,mttud每個I/O操作中都要使用它們。
通道是對原I/O包中流的類比,到任何目的地或者來自任何地方的所有資料都必須通過一個Channel對象。 一個Buffer對象實質上是一個容器物件,發送給一個通道的所有對象都必須首先放到緩衝區中;同樣地,從通道中讀取的任何資料都要讀到緩衝區中。
緩衝區
緩衝區實質上是一個數組。通常它是一個位元組數組,但是也可以使用其他種類的數組。但是一個緩衝區不僅僅是一個數組,其還提供了對資料的結構化訪問,而且還可以跟蹤系統的讀寫進程。
緩衝區類型
- ByteBuffer
- CharBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
每一個Buffer類都是Buffer介面一個執行個體。除了ByteBuffer,每個Buffer類都有一個完全一樣的操作,只是它們所處理的資料類型不一樣,因為大多數標準I/O操作都要用到ByteBuffer,所以它具有所有共用的緩衝區操作以及一些特有的操作。
通道
channel是一個對象,可以通過它讀取或寫入資料。正如前面所說,所有資料都通過Buffer對象來處理。你永遠不會將位元組直接寫入通道中,相反,你是將資料寫入一個包含一個或多個位元組的緩衝區中。同樣,你不會直接從通道中讀取位元組,而是將資料讀取到緩衝區中,再從緩衝區中讀取這個位元組。
通道類型
通道與流的不同之處就在於通道是雙向的,而流只是在一個方向上進行移動,而通過可以用於讀,寫,或者同時讀寫。
NIO中的讀寫
讀寫是I/O的基本過程。從一個通道中讀取很簡單:只需要建立一個緩衝然後讓通道將資料讀取到這個緩衝區中。寫入也很簡單:建立一個緩衝區,用資料填充它,然後讓通道用這些資料來執行寫入操作。
從檔案中讀取
讀取檔案涉及三個步驟:
RandomAccessFile aFile = new RandomAccessFile("testNIO.in", "rw");FileChannel inChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(48);
inChannel.read(buf);
大家可能會注意到,我們不需要告訴通道要讀多少資料到緩衝區,每一個緩衝區都有複雜的內部統計機制,它會跟蹤已經讀了多少資料以及還有多少空間可以容納更多的資料。
寫入檔案
在NIO中寫入檔案類似於從檔案中讀取,首先還是獲得通道。
RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw");FileChannel outChannel = toFile.getChannel();
接著建立緩衝區,並把資料寫入緩衝區中。
String newData = "New String to write to File!";ByteBuffer buf = ByteBuffer.allocate(50);buf.clear();buf.put(newData.getBytes());buf.flip();
最後就是將緩衝區中的內容寫入通道中。
toChannel.write(buf);
在這裡同樣,不需要告訴通道要寫入多少資料。
讀寫結合
下面給出一個讀寫結合的例子,目的是將一個檔案中的內容複寫到另一個檔案中,同樣分為三個基本操作:
ByteBuffer buf = ByteBuffer.allocate(50);
inChannel.read(buf);
outChannel.write(buf);
注意,這個過程不斷重複 讀,寫,讀,寫,直到源檔案結束。
通道轉移
上面的例子給出了普通的複製檔案的思路,其實,如果是FileChannel的話,還可以利用transferFrom()和transferTo()方法來完成檔案的複製。下面直接給出這兩種方法的用法:
/** * FileChannel的transferFrom()方法可以將資料從源通道傳輸到FileChannel中 */ private void channelTransferFrom() { try { RandomAccessFile fromFile = new RandomAccessFile("testNIO1.in", "rw"); FileChannel inChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw"); FileChannel outChannel = toFile.getChannel(); long position = 0; long count = inChannel.size(); outChannel.transferFrom(inChannel, position, count); fromFile.close(); toFile.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * transferTo()方法將資料從FileChannel傳輸到其他的channel中 */ private void channelTransferTo() { try { RandomAccessFile fromFile = new RandomAccessFile("testNIO2.in", "rw"); FileChannel inChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("testNIO2.out", "rw"); FileChannel outChannel = toFile.getChannel(); long position = 0; long count = inChannel.size(); inChannel.transferTo(position, count, outChannel); fromFile.close(); toFile.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
可以發現兩個方法的用法極其相似。
這裡只是給出了NIO的一些基礎,需要深入瞭解的可以看:http://ifeve.com/overview/
本文所涉及到的全部代碼:
package com.jesson.mianshi.nio;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;public class NIODemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub NIODemo nd = new NIODemo(); // nd.fileChannelRead(); // nd.fileChannelWrite(); // nd.channelTransferFrom(); // nd.channelTransferTo(); nd.copyFile(); } /** * NIO讀 */ private void fileChannelRead() { try { RandomAccessFile aFile = new RandomAccessFile("testNIO.in", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); int byteRead = inChannel.read(buf); while (byteRead != -1) { System.out.println("Read " + byteRead); buf.flip(); while (buf.hasRemaining()) { System.out.print((char) buf.get()); } buf.clear(); byteRead = inChannel.read(buf); System.out.println("\n" + byteRead); } aFile.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 向FileChannel寫入資料 */ private void fileChannelWrite() { String newData = "New String to write to File!"; RandomAccessFile toFile = null; try { toFile = new RandomAccessFile("testNIO.out", "rw"); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } FileChannel toChannel = toFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(50); buf.clear(); buf.put(newData.getBytes()); buf.flip(); while (buf.hasRemaining()) { try { toChannel.write(buf); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { toFile.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * FileChannel的transferFrom()方法可以將資料從源通道傳輸到FileChannel中 */ private void channelTransferFrom() { try { RandomAccessFile fromFile = new RandomAccessFile("testNIO1.in", "rw"); FileChannel inChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw"); FileChannel outChannel = toFile.getChannel(); long position = 0; long count = inChannel.size(); outChannel.transferFrom(inChannel, position, count); fromFile.close(); toFile.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * transferTo()方法將資料從FileChannel傳輸到其他的channel中 */ private void channelTransferTo() { try { RandomAccessFile fromFile = new RandomAccessFile("testNIO2.in", "rw"); FileChannel inChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("testNIO2.out", "rw"); FileChannel outChannel = toFile.getChannel(); long position = 0; long count = inChannel.size(); inChannel.transferTo(position, count, outChannel); fromFile.close(); toFile.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 複製檔案 */ private void copyFile() { try { ByteBuffer buf = ByteBuffer.allocate(50); RandomAccessFile fromFile = new RandomAccessFile("testNIO3.in", "rw"); FileChannel inChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("testNIO3.out", "rw"); FileChannel outChannel = toFile.getChannel(); int byteRead = inChannel.read(buf); while (byteRead != -1) { buf.flip(); outChannel.write(buf); buf.clear(); byteRead = inChannel.read(buf); } fromFile.close(); toFile.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
java進階 ------ Java NIO