Java NIO和NIO.2有什麼區別? | baeldung

banq發表於2022-01-27

在本教程中,我們將介紹 Java IO 功能以及它們在不同 Java 版本中的變化。首先,我們將介紹初始 Java 版本中的java.io包。接下來,我們將回顧Java 1.4 中引入的java.nio包。最後,我們將介紹java.nio.file包,通常稱為 NIO.2 包。

 

Java NIO 包

第一個 Java 版本與java.io包一起釋出,引入了一個 File類來訪問檔案系統。File類表示檔案和目錄,並提供對檔案系統的有限操作。可以建立和刪除檔案,檢查它們是否存在,檢查讀/寫訪問等。

它也有一些缺點:

  • 缺少複製方法——要複製一個檔案,我們需要建立兩個File例項並使用一個緩衝區來讀取一個並寫入另一個File例項。
  • 錯誤處理錯誤  ——一些方法返回布林值作為操作成功與否的指示符。
  • 一組有限的檔案屬性——名稱、路徑、讀/寫許可權、可用記憶體大小等等。
  • 阻塞 API——我們的執行緒被阻塞,直到 IO 操作完成。

要讀取檔案,我們需要一個FileInputStream例項來從檔案中讀取位元組:

@Test
public void readFromFileUsingFileIO() throws Exception {
    File file = new File("src/test/resources/nio-vs-nio2.txt");
    FileInputStream in = new FileInputStream(file);
    StringBuilder content = new StringBuilder();
    int data = in.read();
    while (data != -1) {
        content.append((char) data);
        data = in.read();
    }
    in.close();
    assertThat(content.toString()).isEqualTo("Hello from file!");
}

接下來,Java 1.4 引入了捆綁在java.nio包中的非阻塞 IO API (nio 代表新 IO)。引入 NIO 是為了克服java.io包的限制。這個包引入了三個核心類:Channel、Buffer和Selector:

  • Channel

Java NIO Channel是一個允許我們讀取和寫入緩衝區的類。Channel類類似於Streams(這裡我們說的是 IO Streams,而不是 Java 1.8 Streams),但有一些不同之處。Channel是雙向的,而Streams通常是單向的,它們可以非同步讀寫。

Channel類有幾個實現,包括FileChannel用於檔案系統讀/寫,DatagramChannel 用於使用 UDP 在網路上讀/寫,以及SocketChannel用於使用 TCP 在網路上讀/寫。

  • Buffer

Buffer緩衝區是一塊記憶體,我們可以從中讀取或寫入資料。NIO Buffer物件包裝了一個記憶體塊。Buffer類提供了一組與記憶體塊一起工作的功能。要使用Buffer物件,我們需要了解Buffer類的三個主要屬性:容量、位置和限制。

  • 容量定義了記憶體塊的大小。當我們將資料寫入緩衝區時,我們只能寫入有限的長度。當緩衝區已滿時,我們需要讀取資料或清除資料。
  • 位置是我們寫入資料的起點。一個空緩衝區從 0 開始到容量 - 1。另外,當我們讀取資料時,我們從位置值開始。
  • 限制意味著我們如何從緩衝區寫入和讀取。

Buffer類有多種變體。每個原始 Java 型別一個,不包括Boolean型別和MappedByteBuffer

要使用緩衝區,我們需要知道一些重要的方法:

  • allocate(int value)——我們使用這個方法來建立一個特定大小的緩衝區。
  • flip() - 此方法用於從寫入模式切換到讀取模式
  • clear() –清除緩衝區內容的方法
  • compact() –只清除我們已經閱讀的內容的方法
  • rewind() -將位置重置為 0,以便我們可以重新讀取緩衝區中的資料

使用前面描述的概念,讓我們使用Channel和Buffer類從檔案中讀取內容:

@Test
public void readFromFileUsingFileChannel() throws Exception {
    RandomAccessFile file = new RandomAccessFile("src/test/resources/nio-vs-nio2.txt", "r");
    FileChannel channel = file.getChannel();
    StringBuilder content = new StringBuilder();
    ByteBuffer buffer = ByteBuffer.allocate(256);
    int bytesRead = channel.read(buffer);
    while (bytesRead != -1) {
        buffer.flip();
        while (buffer.hasRemaining()) {
            content.append((char) buffer.get());
        }
        buffer.clear();
        bytesRead = channel.read(buffer);
    }
    file.close();
    assertThat(content.toString()).isEqualTo("Hello from file!");
}

在初始化所有需要的物件後,我們從通道讀取到緩衝區。接下來,在 while 迴圈中,我們使用flip()方法標記要讀取的緩衝區,並一次讀取一個位元組,並將其附加到我們的結果中。最後,我們清除資料並讀取另一批。

  • Selector

Java NIO Selector允許我們用一個執行緒管理多個通道。要使用選擇器物件監控多個通道,每個通道例項必須處於非阻塞模式,並且我們必須註冊它。通道註冊後,我們得到一個SelectionKey物件,表示通道和選擇器之間的連線。當我們有多個通道連線到一個選擇器時,我們可以使用select()方法來檢查有多少通道可供使用。呼叫select()方法後,我們可以使用selectedKeys()方法獲取所有準備好的通道。

 

java.nio包引入的變化更多與底層資料 IO 相關。雖然他們允許非阻塞 API,但其他方面仍然存在問題:

  • 對符號連結的有限支援
  • 對檔案屬性訪問的有限支援
  • 缺少更好的檔案系統管理工具

 

Java NIO.2 包

Java 1.7 引入了新的java.nio.file包,也稱為NIO.2 包。此包遵循java.nio包中不支援的非阻塞 IO 的非同步方法。最重要的變化與高階檔案操作有關。它們與Files、Path和Paths類一起新增。最顯著的低階更改是新增了AsynchroniousFileChannel和AsyncroniousSocketChannel。

  • Path

Path物件表示由分隔符分隔的目錄和檔名的分層序列。根元件在最左邊,而檔案在右邊。此類提供實用方法,例如getFileName()、 getParent()等。 Path類還提供解析和相對化方法,幫助構建不同檔案之間的路徑。Paths 類是一組靜態實用程式方法,它們接收String或URI以建立Path例項。

  • Files

Files類提供了使用前面描述的Path類並對檔案、目錄和符號連結進行操作的實用方法。它還提供了一種使用readAttributes()方法讀取許多檔案屬性的方法。

最後,讓我們看看 NIO.2 在讀取檔案時與之前的 IO 版本相比如何:

@Test
public void readFromFileUsingNIO2() throws Exception {
    List<String> strings = Files.readAllLines(Paths.get("src/test/resources/nio-vs-nio2.txt"));
    assertThat(strings.get(0)).isEqualTo("Hello from file!");
}

在本文中,我們介紹了java.nio和java.nio.file包的基礎知識。我們可以看到,NIO.2 並不是 NIO 包的新版本。NIO 包引入了用於非阻塞 IO 的低階 API,而 NIO.2 引入了更好的檔案管理。這兩個包不是同義詞,而是相互補充。與往常一樣,所有程式碼示例都可以在 GitHub 上找到

 

相關文章