1. 概述
本文将深入探讨 Java 7 引入的 NIO2(New I/O 2)中的 Path API。作为 Java 7 最重要的新特性之一,Path API 与传统 File API 共同构成了全新的文件系统操作体系,为开发者提供了更强大、更灵活的文件路径处理能力。
2. 环境准备
NIO2 的核心功能集中在 java.nio.file
包中。使用 Path API 只需导入该包:
import java.nio.file.*;
为确保代码在不同环境中的兼容性,我们获取用户主目录作为基准路径:
private static String HOME = System.getProperty("user.home");
Paths
类是所有路径操作的入口点,用于创建和操作文件/目录路径。重要提示:路径操作本质上是语法层面的,不会影响底层文件系统,文件系统也不会影响操作的成败。这意味着操作不存在的路径不会导致失败。
3. 路径操作基础
Path
类是文件系统路径的程序化表示,包含文件名和目录列表,用于检查、定位和操作文件。java.nio.file.Paths
(注意复数形式)是创建 Path
对象的标准工具类,提供两种静态方法:
从路径字符串创建:
Path path = Paths.get("path string");
路径分隔符(/ 或 \)会被自动适配底层文件系统
从 URI 对象创建:
Path path = Paths.get(URI object);
4. 创建路径对象
通过路径字符串创建 Path
对象:
@Test
public void givenPathString_whenCreatesPathObject_thenCorrect() {
Path p = Paths.get("/articles/baeldung");
assertEquals("\\articles\\baeldung", p.toString());
}
get()
方法支持可变参数,自动拼接路径部分:
@Test
public void givenPathParts_whenCreatesPathObject_thenCorrect() {
Path p = Paths.get("/articles", "baeldung");
assertEquals("\\articles\\baeldung", p.toString());
}
✅ 无需手动添加分隔符,API 会自动处理
5. 获取路径信息
Path
可视为名称元素的序列。例如路径 E:\baeldung\articles\java
包含三个元素:
- 索引 0:
baeldung
(最高级目录) - 索引 1:
articles
- 索引 2:
java
(最低级元素,称为文件名)
获取文件名:
@Test
public void givenPath_whenRetrievesFileName_thenCorrect() {
Path p = Paths.get("/articles/baeldung/logs");
Path fileName = p.getFileName();
assertEquals("logs", fileName.toString());
}
按索引获取元素:
@Test
public void givenPath_whenRetrievesNameByIndex_thenCorrect() {
Path p = Paths.get("/articles/baeldung/logs");
Path name0 = getName(0);
Path name1 = getName(1);
Path name2 = getName(2);
assertEquals("articles", name0.toString());
assertEquals("baeldung", name1.toString());
assertEquals("logs", name2.toString());
}
获取子路径:
@Test
public void givenPath_whenCanRetrieveSubsequenceByIndex_thenCorrect() {
Path p = Paths.get("/articles/baeldung/logs");
Path subPath1 = p.subpath(0,1);
Path subPath2 = p.subpath(0,2);
assertEquals("articles", subPath1.toString());
assertEquals("articles\\baeldung", subPath2.toString());
assertEquals("articles\\baeldung\\logs", p.subpath(0, 3).toString());
assertEquals("baeldung", p.subpath(1, 2).toString());
assertEquals("baeldung\\logs", p.subpath(1, 3).toString());
assertEquals("logs", p.subpath(2, 3).toString());
}
获取父路径:
@Test
public void givenPath_whenRetrievesParent_thenCorrect() {
Path p1 = Paths.get("/articles/baeldung/logs");
Path p2 = Paths.get("/articles/baeldung");
Path p3 = Paths.get("/articles");
Path p4 = Paths.get("/");
Path parent1 = p1.getParent();
Path parent2 = p2.getParent();
Path parent3 = p3.getParent();
Path parent4 = p4.getParenth();
assertEquals("\\articles\\baeldung", parent1.toString());
assertEquals("\\articles", parent2.toString());
assertEquals("\\", parent3.toString());
assertEquals(null, parent4);
}
获取根路径:
@Test
public void givenPath_whenRetrievesRoot_thenCorrect() {
Path p1 = Paths.get("/articles/baeldung/logs");
Path p2 = Paths.get("c:/articles/baeldung/logs");
Path root1 = p1.getRoot();
Path root2 = p2.getRoot();
assertEquals("\\", root1.toString());
assertEquals("c:\\", root2.toString());
}
6. 规范化路径
文件系统常用 .
表示当前目录,..
表示父目录。路径可能包含冗余信息,例如:
/baeldung/./articles
/baeldung/authors/../articles
/baeldung/articles
以上路径都指向相同位置 /baeldung/articles
。规范化操作可消除这些冗余:
@Test
public void givenPath_whenRemovesRedundancies_thenCorrect1() {
Path p = Paths.get("/home/./baeldung/articles");
Path cleanPath = p.normalize();
assertEquals("\\home\\baeldung\\articles", cleanPath.toString());
}
@Test
public void givenPath_whenRemovesRedundancies_thenCorrect2() {
Path p = Paths.get("/home/baeldung/../articles");
Path cleanPath = p.normalize();
assertEquals("\\home\\articles", cleanPath.toString());
}
7. 路径转换
转换为浏览器可访问的 URI
@Test
public void givenPath_whenConvertsToBrowseablePath_thenCorrect() {
Path p = Paths.get("/home/baeldung/articles.html");
URI uri = p.toUri();
assertEquals(
"file:///E:/home/baeldung/articles.html",
uri.toString());
}
转换为绝对路径
@Test
public void givenPath_whenConvertsToAbsolutePath_thenCorrect() {
Path p = Paths.get("/home/baeldung/articles.html");
Path absPath = p.toAbsolutePath();
assertEquals(
"E:\\home\\baeldung\\articles.html",
absPath.toString());
}
⚠️ 若路径已是绝对路径,则直接返回原路径:
@Test
public void givenAbsolutePath_whenRetainsAsAbsolute_thenCorrect() {
Path p = Paths.get("E:\\home\\baeldung\\articles.html");
Path absPath = p.toAbsolutePath();
assertEquals(
"E:\\home\\baeldung\\articles.html",
absPath.toString());
}
转换为真实路径
toRealPath()
会解析符号链接并验证路径存在性:
@Test
public void givenExistingPath_whenGetsRealPathToFile_thenCorrect() {
Path p = Paths.get(HOME);
Path realPath = p.toRealPath();
assertEquals(HOME, realPath.toString());
}
❌ 若路径不存在,抛出
NoSuchFileException
:
@Test(expected = NoSuchFileException.class)
public void givenInExistentPath_whenFailsToConvert_thenCorrect() {
Path p = Paths.get("E:\\home\\baeldung\\articles.html");
p.toRealPath();
}
8. 合并路径
使用 resolve()
合并路径。当传入相对路径时,会追加到原路径:
@Test
public void givenTwoPaths_whenJoinsAndResolves_thenCorrect() {
Path p = Paths.get("/baeldung/articles");
Path p2 = p.resolve("java");
assertEquals("\\baeldung\\articles\\java", p2.toString());
}
⚠️ 传入绝对路径时,直接返回传入路径:
@Test
public void givenAbsolutePath_whenResolutionRetainsIt_thenCorrect() {
Path p = Paths.get("/baeldung/articles");
Path p2 = p.resolve("C:\\baeldung\\articles\java");
assertEquals("C:\\baeldung\\articles\\java", p2.toString());
}
⚠️ 传入带根元素的路径时,同样直接返回:
@Test
public void givenPathWithRoot_whenResolutionRetainsIt_thenCorrect2() {
Path p = Paths.get("/baeldung/articles");
Path p2 = p.resolve("/java");
assertEquals("\\java", p2.toString());
}
9. 相对化路径
relativize()
创建两个路径间的直接路径。例如同级目录:
@Test
public void givenSiblingPaths_whenCreatesPathToOther_thenCorrect() {
Path p1 = Paths.get("articles");
Path p2 = Paths.get("authors");
Path p1_rel_p2 = p1.relativize(p2);
Path p2_rel_p1 = p2.relativize(p1);
assertEquals("..\\authors", p1_rel_p2.toString());
assertEquals("..\\articles", p2_rel_p1.toString());
}
非同级目录示例:
@Test
public void givenNonSiblingPaths_whenCreatesPathToOther_thenCorrect() {
Path p1 = Paths.get("/baeldung");
Path p2 = Paths.get("/baeldung/authors/articles");
Path p1_rel_p2 = p1.relativize(p2);
Path p2_rel_p1 = p2.relativize(p1);
assertEquals("authors\\articles", p1_rel_p2.toString());
assertEquals("..\\..", p2_rel_p1.toString());
}
10. 比较路径
路径相等性
@Test
public void givenTwoPaths_whenTestsEquality_thenCorrect() {
Path p1 = Paths.get("/baeldung/articles");
Path p2 = Paths.get("/baeldung/articles");
Path p3 = Paths.get("/baeldung/authors");
assertTrue(p1.equals(p2));
assertFalse(p1.equals(p3));
}
路径前缀检查
@Test
public void givenPath_whenInspectsStart_thenCorrect() {
Path p1 = Paths.get("/baeldung/articles");
assertTrue(p1.startsWith("/baeldung"));
}
路径后缀检查
@Test
public void givenPath_whenInspectsEnd_thenCorrect() {
Path p1 = Paths.get("/baeldung/articles");
assertTrue(p1.endsWith("articles"));
}
11. 总结
本文系统介绍了 Java 7 NIO2 中的 Path API,涵盖路径创建、信息检索、规范化、转换、合并、相对化和比较等核心操作。这些 API 显著提升了文件路径处理的灵活性和可靠性,是现代 Java 开发中不可或缺的工具。
完整代码示例可在 GitHub 项目 中获取。