跨平台分隔符差異
| 作業系統 | 路徑分隔符 | 路徑列表分隔符 | 範例 |
| Windows | \ (反斜線) | ; (分號) | C:\Users\name\file.txt |
| Linux/macOS/Unix | / (正斜線) | : (冒號) | /home/name/file.txt |
Java 核心常量
public class PathSeparators {
public static void main(String[] args) {
// 1. 檔案路徑分隔符 (單字元字串)
System.out.println("file.separator = " + File.separator); // Windows: \, Linux: /
System.out.println("File.separatorChar = " + File.separatorChar); // Windows: \, Linux: /
// 2. 路徑列表分隔符 (CLASSPATH 等)
System.out.println("path.separator = " + File.pathSeparator); // Windows: ;, Linux: :
System.out.println("File.pathSeparatorChar = " + File.pathSeparatorChar);
// 3. 系統屬性 (同上)
System.out.println("System.getProperty(\"file.separator\") = " + System.getProperty("file.separator"));
System.out.println("System.getProperty(\"path.separator\") = " + System.getProperty("path.separator"));
}
}
路徑構建的正確姿勢
❌ 錯誤:硬編碼分隔符
// Windows 才能跑,Linux 會把 "C:\temp\file.txt" 當成單一檔名
File f1 = new File("C:\\temp\\file.txt");
File f2 = new File("/home/user/file.txt"); // Linux 才能跑
✅ 正確:使用 File 建構子自動處理
// 多參數建構子:自動插入正確分隔符 (推薦)
File f1 = new File("C:", "temp", "file.txt"); // Windows: C:\temp\file.txt
File f2 = new File("/home", "user", "file.txt"); // Linux: /home/user/file.txt
// 父目錄 + 子路徑
File dir = new File("C:", "temp");
File f3 = new File(dir, "file.txt");
✅ 現代 Java 7+:Path API (強型別、不可變、功能豐富)
import java.nio.file.*;
Path p1 = Paths.get("C:", "temp", "file.txt"); // Windows
Path p2 = Paths.get("/home", "user", "file.txt"); // Linux
Path p3 = Path.of("C:", "temp", "file.txt"); // Java 11+ 簡寫
// 相對路徑解析
Path base = Paths.get("/home/user");
Path resolved = base.resolve("docs/file.txt"); // /home/user/docs/file.txt
// 相對化
Path from = Paths.get("/home/user");
Path to = Paths.get("/home/user/docs/file.txt");
Path relativized = from.relativize(to); // docs/file.txt
// 正規化 (處理 . 和 ..)
Path messy = Paths.get("/home/user/../user/./docs/file.txt");
Path clean = messy.normalize(); // /home/user/docs/file.txt
Windows 路徑特殊處理
反斜線轉義問題
// Java 字串字面值中 \ 為轉義字元
String s1 = "C:\\temp\\file.txt"; // 正確:雙重轉義
String s2 = "C:\temp\file.txt"; // 錯誤:\t 被解析為 Tab,\f 為換頁
// 原始字串 (Java 13+ 文字區塊 / Java 15+ 正式)
String path = """
C:\temp\file.txt
"""; // 無需轉義,但結尾會有換行需 strip()
// 正則表達式中雙重轉義
String regex = "C:\\\\temp\\\\file\\.txt"; // 字串層 \\ → 正則層 \\ → 匹配字面 \
UNC 路徑 (網路共享)
// \\server\share\path
Path unc = Paths.get("\\\\server", "share", "path"); // 正確
// 或
File uncFile = new File("\\\\server\\share\\path"); // 字串需四重轉義
常見操作對比表
| 操作 | File (舊 API) | Path (新 API) | 備註 |
| 建立路徑 | new File(parent, child) | Paths.get(...) / Path.of(...) | Path 更安全 |
| 取得檔名 | file.getName() | path.getFileName().toString() | |
| 取得父目錄 | file.getParentFile() | path.getParent() | |
| 轉絕對路徑 | file.getAbsoluteFile() | path.toAbsolutePath() | |
| 正規化 | file.getCanonicalFile() (拋 IOException) | path.normalize() (純記憶體) | Path 更快 |
| 檢查存在 | file.exists() | Files.exists(path) | |
| 建立目錄 | file.mkdirs() | Files.createDirectories(path) | Path 拋異常可追蹤 |
| 讀取所有行 | 手動 BufferedReader | Files.readAllLines(path) | 一行搞定 |
| 走訪目錄樹 | 遞迴 listFiles() | Files.walk(path) (Stream) | 函數式風格 |
最佳實踐總結
- 新專案全面使用 Path API:Java 7+ 標準、型別安全、不可變、Stream 整合
- 動態構建路徑:
Paths.get(base, "sub", "file.txt"),絕不手工拼接字串
- 跨平台測試:CI/CD 必須在 Windows/Linux/macOS 皆驗證
- 配置外部化:基礎目錄用環境變數/配置檔,
Paths.get(System.getenv("APP_HOME"), "data")
- 避免 current directory 依賴:
new File("relative/path") 依賴 user.dir,不穩定
- 單元測試用臨時目錄:
@TempDir Path tempDir (JUnit 5) 或 Files.createTempDirectory()
遷移檢查清單
// 檢查專案中是否有硬編碼分隔符
grep -r "File.separator" src/ --include="*.java" | grep -v "File.separatorChar"
grep -r "\\\\\\\\" src/ --include="*.java" # 找雙重轉義 (可能硬編碼)
grep -r "\"/" src/ --include="*.java" # 找正斜線字面值