天天看點

Java:前程似錦的 NIO 2.0-1

Java 之是以能夠霸占程式設計語言的榜首,其強大、豐富的類庫功不可沒,幾乎所有的程式設計問題都能在其中找到解決方案。但在早期的版本當中,輸入輸出(I/O)流并不那麼令開發者感到愉快:

1)JDK 1.4 之前的 I/O 沒有緩沖區的概念、不支援正規表達式、支援的字元集編碼有限等等;

2)JDK 1.4 的時候引入了非阻塞 I/O,也就是 NIO 1.0,但周遊目錄很困難,不支援檔案系統的非阻塞操作等等。

為了突破這些限制,JDK 1.7 的時候引入了新的 NIO,也就是本篇文章的主角——NIO 2.0。

01、基石:Path

Path 既可以表示一個目錄,也可以表示一個檔案,就像 File 那樣——當然了,Path 就是用來取代 File 的。

1)可以通過 Paths.get() 建立一個 Path 對象,此時 Path 并沒有真正在實體磁盤上建立;參數既可以是一個檔案名,也可以是一個目錄名;絕對路徑或者相對路徑均可。

2)可以通過 Files.notExists() 确認 Path(目錄或者檔案) 是否已經存在。

3)可以通過 Files.createDirectory() 建立目錄,此時目錄已經在實體磁盤上建立成功,可通過資料總管檢視到。

4)可以通過 Files.createFile() 建立檔案,此時檔案已經在實體磁盤上建立成功,可通過資料總管檢視到。

5)可以通過 toAbsolutePath() 檢視 Path 的絕對路徑。

6)可以通過 resolve() 将 Path 連接配接起來,參數可以是一個新的 Path 對象,也可以是對應的字元串。

具體的代碼如下:

public class Wanger {

    public static void main(String[] args) {
        // 相對路徑
        Path dir = Paths.get("chenmo");

        // 輸出 dir 的絕對路徑
        System.out.println(dir.toAbsolutePath()); // 輸出:D:\program\java.git\java_demo\chenmo

        if (Files.notExists(dir)) {
            try {
                // 如果目錄不存在,則建立目錄
                Files.createDirectory(dir);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        
        // 這時候 chenmo.txt 檔案并未建立
        // 通過 resolve 方法把 dir 和 chenmo.txt 連結起來
        Path file = dir.resolve("chenmo.txt");

        // 輸出 file 的絕對路徑
        System.out.println(file.toAbsolutePath()); // 輸出:D:\program\java.git\java_demo\chenmo\chenmo.txt

        if (Files.notExists(file)) {
            try {
                // 如果檔案不存在,則建立檔案
                Files.createFile(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}

      

如果要将 File 轉換為 Path,可以通過 File 類的 toPath() 方法完成。代碼示例如下:

File file = new File("沉默王二.txt");

Path path = file.toPath();

如果要将 Path 轉換為 File,可以通過 Path 類的 toFile() 方法完成。代碼示例如下:

Path path = Paths.get("沉默王二.txt");

File file = path.toFile();

02、處理目錄

NIO 2.0 新增的 java.nio.file.DirectoryStream<T> 接口可以非常友善地查找目錄中的(符合某種規則的)檔案,比如說我們要查找 chenmo 目錄下的 txt 字尾的檔案,代碼示例如下:

// 相對路徑
Path dir = Paths.get("chenmo");
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt")) {
    for (Path entry : stream) {
  System.out.println(entry.getFileName());
    }
} catch (IOException e) {
    e.printStackTrace();
}      

1)Files.newDirectoryStream(Path dir, String glob) 會傳回一個過濾後的 DirectoryStream( 目錄流,),第一個參數為目錄,第二個參數為 glob 表達式,比如 *.txt 表示所有 txt 字尾的檔案。

2)由于 DirectoryStream 繼承了 Closeable 接口,是以它可以配合 try-with-resources 文法寫出更安全的代碼,目錄流會自動調用 close 方法關閉流,釋放與流相關的資源,不需要再通過 finally 進行主動關閉。

3)DirectoryStream 被稱為目錄流,允許友善地使用 for-each 結構來周遊目錄。

03、處理目錄樹

目錄樹意味着一個目錄裡既有檔案也有子目錄,也可能都沒有,也可能有其一。NIO 2.0 可以很友善地周遊一顆目錄樹,并操作符合條件的檔案;這其中關鍵的一個方法就是 Files 類的 walkFileTree,其定義如下:

public static Path walkFileTree(Path start, FileVisitor<? super Path> visitor)
        throws IOException
    {
        return walkFileTree(start,
                            EnumSet.noneOf(FileVisitOption.class),
                            Integer.MAX_VALUE,
                            visitor);
    }      

第二個參數 FileVisitor 被稱為檔案通路器接口,它實作起來非常複雜,要實作 5 個方法呢,但幸好 JDK 的設計者提供了一個預設的實作類 SimpleFileVisitor,如果我們隻想從目錄樹中找到 txt 字尾的檔案,可以這樣做:

// 相對路徑
Path dir = Paths.get("chenmo");
try {
    Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
  @Override
  public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
    if (file.toString().endsWith(".txt")) {
    System.out.println(file.getFileName());
    }
    return FileVisitResult.CONTINUE;
  }
    });
} catch (IOException e) {
    e.printStackTrace();
}      

通過建立匿名内部類來重寫 SimpleFileVisitor 的 visitFile 方法,如果字尾名為 txt 就列印出來。