CodeGym /課程 /JAVA 25 SELF /java.io 與 java.nio 基礎:差異與 API 演進

java.io 與 java.nio 基礎:差異與 API 演進

JAVA 25 SELF
等級 35, 課堂 0
開放

1. Java 如何學會處理檔案

Java 與檔案:一切從何開始

很久以前,在遙遠的 1996 年,語言的開發者提出了第一種處理檔案的方式 — 套件 java.io。它引入了 FileFileInputStreamFileOutputStreamReaderWriter 等類別。這些類別讓我們可以進行各種檔案操作:判斷檔案是否存在、取得大小、修改時間,並處理目錄。

然而,就像許多第一版一樣,它並不完美 — 有很多事情本可以做得更便利與更安全。

舊 API (java.io) 的限制

  • File 不是檔案,而是「捷徑」。 類別 File 並不會讀寫內容;它描述的是路徑與中繼資料。要讀/寫必須使用獨立的串流:FileInputStream/FileOutputStreamReader/Writer
  • 路徑處理令人頭疼。"C:\\Users\\" + user + "\\Documents" 這樣手動拼接路徑,在 Windows 與 Linux 之間移植時常常出錯。
  • 不支援符號連結。 舊 API 不理解符號連結(symlink),可能錯誤地與之互動。
  • 對檔案屬性的支援薄弱. 不易取得存取權限、擁有者、隱藏等旗標等資訊。
  • 對大型檔案效率不佳。 傳統串流對大量資料不夠便利與快速,且缺乏非同步 I/O。

java.nio.file 的出現(NIO.2,Java 7)

在 Java 7 出現了新套件:java.nio.file(又稱 NIO.2)。它帶來了:

  • Path — 現代的路徑抽象(包含 ZIP、雲端與網路檔案系統中的路徑)。
  • Files — 用於讀寫、複製、刪除的靜態工具方法。
  • FileSystem — 不僅能操作本機磁碟,還能處理其他檔案系統。
  • 支援符號連結、延伸屬性與存取權限。
  • 非同步 I/O 與在大量資料上的更佳效能.
  • 更方便地操作目錄與串流式 API。

NIO 是「New Input/Output」的縮寫;即使已經問世十多年,對 Java 而言它仍是現代的標準。

2. 做法比較: java.io vs java.nio.file

File 類(java.io

  • 表示檔案或目錄的路徑與其中繼資料。
  • 不會讀/寫檔案內容。
  • 可取得是否存在、大小、型別(檔案/目錄)、絕對路徑等資訊。
import java.io.File;

public class ExampleIO {
    public static void main(String[] args) {
        File file = new File("example.txt");
        if (file.exists()) {
            System.out.println("檔案存在!");
            System.out.println("大小:" + file.length() + " 位元組");
        }
    }
}

Path 類(java.nio.file

  • 現代的路徑抽象,能自然地組合與正規化。
  • 可表示本機、封存(壓縮檔)以及網路路徑。
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ExampleNIOPath {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("example.txt");
        if (Files.exists(path)) {
            System.out.println("找到檔案!");
            System.out.println("大小:" + Files.size(path) + " 位元組");
        }
    }
}

Files 類(java.nio.file

  • 一組用於整體或分段讀/寫的靜態方法。
  • 檔案的複製、刪除與移動。
  • 建立/刪除目錄。
  • 存取擴充屬性。
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class ExampleNIOFiles {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("example.txt");
        List<String> lines = Files.readAllLines(path);
        for (String line : lines) {
            System.out.println(line);
        }
    }
}

比較表

功能
java.io.File
java.nio.file.Path + Files
路徑表示
讀寫內容 是(經由 Files)
路徑操作 不方便 方便(resolve, normalize)
符號連結
檔案屬性 受限 擴充
非同步 I/O 是(NIO.2)
跨平台性 部分 極佳

3. 何時該用哪一個?

舊 API (java.io):什麼時候需要?

  • 為了支援 legacy 程式碼。 若專案始於 Java 7 之前,且各處都使用 FileFileInputStreamFileOutputStream,可能就得維持相容性。
  • 在新專案中 — 不建議。 今天還使用「老前輩」File,就好比用 VHS 錄影帶看影片。

新 API (java.nio.file):現代標準

  • 在新專案中一律優先選用 PathFiles
  • 更簡潔、安全、強大,且更容易與串流、lambda 與 try-with-resources 整合。

速查備忘

  • 讀取、寫入、複製、刪除? — 透過 Files
  • 路徑操作(合併、正規化)? — 透過 Path
  • 大小、日期、存取權限?Files.getAttribute() 與相關方法。
  • 遍歷目錄(遞迴)?Files.walkFiles.list

4. 範例:舊/新 API 的程式碼長什麼樣

範例 1: 檢查檔案是否存在

舊方法:

import java.io.File;

File file = new File("data.txt");
if (file.exists()) {
    System.out.println("找到檔案!");
}

新方法:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

Path path = Paths.get("data.txt");
if (Files.exists(path)) {
    System.out.println("找到檔案!");
}

範例 2: 讀取檔案的所有行

舊方法:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

File file = new File("data.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
reader.close();

新方法:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

Path path = Paths.get("data.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
    System.out.println(line);
}

說明: 新方法更短且更安全;使用 try-with-resources 後,資源管理更為簡單。

範例 3: 將字串寫入檔案

舊方法:

import java.io.FileWriter;

FileWriter writer = new FileWriter("output.txt");
writer.write("哈囉,檔案!");
writer.close();

新方法:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

Path path = Paths.get("output.txt");
Files.write(path, "哈囉,檔案!".getBytes());

5. 實用細節

為什麼 Java 要徹底重做?

  • 安全與可靠性。 更少資源關閉與路徑處理上的錯誤。
  • 跨平台性。 對 Windows、Linux 甚至 ZIP 封存提供一致的類別。
  • 易於擴充。 更容易加入雲端與網路檔案系統的支援。
  • 效能。 在大型檔案與非同步操作上表現更佳。
  • 與現代 Java 生態相容。 Lambda、Stream、try-with-resources

如何從舊 API 過渡到新 API?

幾乎隨時都能把 File 轉成 Path,反之亦然:

File file = new File("data.txt");
Path path = file.toPath();
File file2 = path.toFile();

遇到舊程式碼該怎麼辦?

  • 別慌:一切都可以做得更簡單、更現代。
  • 若有餘裕 — 改寫為 java.nio.file
  • 若無法立即改寫 — 使用橋接(toPath()toFile())並逐步遷移。

6. 最後一個範例:迷你應用程式

範例: 檢查檔案是否存在,並印出其大小。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileInfo {
    public static void main(String[] args) {
        Path path = Paths.get("test.txt");
        if (Files.exists(path)) {
            try {
                long size = Files.size(path);
                System.out.println("找到檔案。大小:" + size + " 位元組");
            } catch (Exception e) {
                System.out.println("取得檔案大小時發生錯誤:" + e.getMessage());
            }
        } else {
            System.out.println("找不到檔案!");
        }
    }
}

我們在此使用了:

  • Path — 路徑表示。
  • Files.exists() — 檢查是否存在。
  • Files.size() — 取得大小。

7. 使用檔案 API 時的常見錯誤

錯誤 №1: 以為 File 能夠讀寫內容。 其實 File 只是「捷徑」。要讀/寫請使用 FileInputStream/FileOutputStream(舊 API)或 Files 的工具方法(新 API)。

錯誤 №2: 用 + 或斜線手動拼接路徑。 這在不同作業系統上會造成錯誤。請使用 Path.resolve(),或以多個參數呼叫 Paths.get(...)

錯誤 №3: 忘記關閉串流。 在舊 API 中,這是資源外洩的常見原因。在新 API 中,許多透過 Files 的操作不會建立串流;若需要,請使用 try-with-resources

錯誤 №4: 在新專案仍使用舊 API。 若要啟動新專案 — 請選擇 java.nio.file。只有在需要相容於 legacy 程式碼時,才有理由回到 java.io

1
任務
JAVA 25 SELF, 等級 35, 課堂 0
上鎖
我的秘密日記在哪裡? 🕵️‍♀️
我的秘密日記在哪裡? 🕵️‍♀️
1
任務
JAVA 25 SELF, 等級 35, 課堂 0
上鎖
您的遊戲存檔有多大? 💾
您的遊戲存檔有多大? 💾
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION