Java NIO Path 接口是 Java 7 中引入的,用于表示文件系统中的路径。它支持创建绝对路径和相对路径,其中相对路径可使用 ... 来分别表示当前目录和父目录。Path 实例通过 Paths.get() 工厂方法创建。此外,Path 提供了 relativize() 方法来计算两个路径之间的相对关系,以及 normalize() 方法来消除路径中的冗余符号。它在功能上替代了传统的 java.io.File 类,提供了更现代的路径处理方式。

博主博客

Java Path 接口是 Java NIO 2 更新的一部分,该更新随 Java 6 和 Java 7 引入。Path 接口在 Java 7 中被添加到 Java NIO 中,位于 java.nio.file 包中,因此其完整限定名是 java.nio.file.Path

Java Path 实例表示文件系统中的一个路径。该路径可以指向文件或目录,可以是绝对路径或相对路径。

  • 绝对路径 包含从文件系统根目录到它所指向的文件或目录的完整路径。
  • 相对路径 包含相对于某个其他路径到文件或目录的路径。相对路径可能听起来有些令人困惑,但请别担心,本教程后续会详细解释。

注意:请不要将文件系统路径与某些操作系统中的 PATH 环境变量混淆。java.nio.file.Path 接口与环境变量 PATH 无关。

在许多方面,java.nio.file.Path 接口类似于 java.io.File 类,但存在一些细微差别。不过,在许多情况下,您可以用 Path 接口来替代 File 类。

创建 Path 实例

要使用 java.nio.file.Path 实例,必须先创建它。您可以使用 Paths 类(java.nio.file.Paths)中的静态方法 Paths.get() 来创建 Path 实例。下面是一个示例:

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

public class PathExample {
    public static void main(String[] args) {
        Path path = Paths.get("c:\\data\\myfile.txt");
    }
}

注意示例顶部的两条 import 语句。要使用 Path 接口和 Paths 类,必须先导入它们。

其次,注意 Paths.get("c:\\data\\myfile.txt") 方法调用。正是对 Paths.get() 方法的调用创建了 Path 实例。换句话说,Paths.get() 方法是 Path 实例的工厂方法。

创建绝对路径

通过以绝对文件路径作为参数调用 Paths.get() 工厂方法来创建绝对路径。下面是一个创建表示绝对路径的 Path 实例的示例:

Path path = Paths.get("c:\\data\\myfile.txt");

绝对路径是 c:\data\myfile.txt。双反斜杠 \\ 在 Java 字符串中是必需的,因为 \ 是一个转义字符,意味着它后面的字符表示字符串中该位置实际的字符。通过写 \\,您告诉 Java 编译器在字符串中写入一个单独的 \ 字符。

上面的路径是一个 Windows 文件系统路径。在 Unix 系统(Linux、macOS、FreeBSD 等)上,上述绝对路径可能如下所示:

Path path = Paths.get("/home/jakobjenkov/myfile.txt");

此时的绝对路径是 /home/jakobjenkov/myfile.txt

如果您在 Windows 机器上使用这种以 / 开头的路径,该路径将被解释为相对于当前驱动器。例如,路径 /home/jakobjenkov/myfile.txt 可能被解释为位于 C 盘,对应的完整路径将是:C:/home/jakobjenkov/myfile.txt

创建相对路径

相对路径是从一个路径(基路径)指向目录或文件的路径。相对路径的完整路径(绝对路径)是通过将基路径与相对路径组合而得出的。

Java NIO Path 类也可用于处理相对路径。您可以使用 Paths.get(basePath, relativePath) 方法创建相对路径。以下是 Java 中两个相对路径的示例:

Path projects = Paths.get("d:\\data", "projects");
Path file     = Paths.get("d:\\data", "projects\\a-project\\myfile.txt");

第一个示例创建了一个指向目录 d:\data\projects 的 Java Path 实例。
第二个示例创建了一个指向文件 d:\data\projects\a-project\myfile.txt 的 Path 实例。

处理相对路径时,可以在路径字符串中使用两种特殊代码:

  • . (当前目录)
  • .. (上级目录)

. 代码 表示“当前目录”。例如,如果您创建如下相对路径:

Path currentDir = Paths.get(".");System.out.println(currentDir.toAbsolutePath());

那么此 Java Path 实例对应的绝对路径将是执行上述代码的应用程序所在的目录。

如果 . 用在路径字符串的中间,它仅表示路径在那一刻所指向的同一目录。以下是一个说明这一点的 Path 示例:

Path currentDir = Paths.get("d:\\data\\projects\\.\\a-project");

此路径将对应于:d:\data\projects\a-project

.. 代码 表示“父目录”或“向上一级目录”。以下是一个说明这一点的 Java Path 示例:

Path parentDir = Paths.get("..");

此示例创建的 Path 实例将对应于运行此代码的应用程序启动目录的父目录。

如果您在路径字符串的中间使用 .. 代码,它将在该点对应于向上改变一级目录。例如:

String path = "d:\\data\\projects\\a-project\\..\\another-project";
Path parentDir2 = Paths.get(path);

此示例创建的 Java Path 实例将对应于以下绝对路径:
d:\data\projects\another-project

a-project 目录之后的 .. 代码将目录向上改变到父目录 projects,然后路径从那里向下引用到 another-project 目录。

... 代码也可以与双字符串的 Paths.get() 方法结合使用。以下是两个展示简单用法的 Java Paths.get() 示例:

Path path1 = Paths.get("d:\\data\\projects", ".\\a-project");
Path path2 = Paths.get("d:\\data\\projects\\a-project", "..\\another-project");

Java NIO Path 类还有更多处理相对路径的方法,本教程后续会详细介绍。

relativize()

Java Path 的 relativize() 方法可以创建一个新的 Path,表示第二个 Path 相对于第一个 Path 的位置。例如,对于路径 /data/data/subdata/subsubdata/myfile.txt,第二个路径可以表示为相对于第一个路径的 /subdata/subsubdata/myfile.txt。请看以下示例:

Path basePath = Paths.get("/data");
Path path     = Paths.get("/data/subdata/subsubdata/myfile.txt");

Path basePathToPath = basePath.relativize(path);
Path pathToBasePath = path.relativize(basePath);

System.out.println(basePathToPath);
System.out.println(pathToBasePath);

此示例的输出将是:

subdata/subsubdata/myfile.txt
../../..

如果将相对路径 subdata/subsubdata/myfile.txt 添加到目录路径 /data,您将得到完整路径 /data/subdata/subsubdata/myfile.txt
如果将相对路径 ../../.. 添加到完整文件路径 /data/subdata/subsubdata/myfile.txt,您将得到 /data

注意:在尝试计算从一个 Path 到另一个 Path 的相对路径时,不能混合使用相对路径和绝对路径。relativize() 方法将抛出异常。因此,以下操作是不允许的:

Path basePath = Paths.get("/data");
Path path     = Paths.get("myfile.txt");

Path basePathToPath = basePath.relativize(path);

如果尝试这样做,您将得到类似以下的异常:

Exception in thread "main" java.lang.IllegalArgumentException: 'other' is different type of Path
    at java.base/sun.nio.fs.WindowsPath.relativize(WindowsPath.java:400)
    at java.base/sun.nio.fs.WindowsPath.relativize(WindowsPath.java:42)
    at com.jenkov.java.nio.PathExamples.main(PathExamples.java:33)

normalize()

Path 接口的 normalize() 方法可以规范化路径。规范化意味着它会移除路径字符串中间所有的 ... 代码,并解析路径字符串所引用的实际路径。以下是一个 Java Path.normalize() 的示例:

String originalPath = "d:\\data\\projects\\a-project\\..\\another-project";

Path path1 = Paths.get(originalPath);
System.out.println("path1 = " + path1);

Path path2 = path1.normalize();
System.out.println("path2 = " + path2);

此 Path 示例首先创建一个包含 .. 代码的路径字符串。然后,示例从此路径字符串创建一个 Path 实例并打印出来(实际上是打印 Path.toString())。
接着,示例在创建的 Path 实例上调用 normalize(),该方法返回一个新的 Path 实例。这个新的、规范化的 Path 实例随后也被打印出来。

以下是上述示例的输出:

path1 = d:\data\projects\a-project\..\another-project
path2 = d:\data\projects\another-project

如您所见,规范化后的路径不包含 a-project\.. 部分,因为这是冗余的。被移除的部分并未对最终的绝对路径添加任何信息。

参考文献