这些Java教程是针对JDK 8编写的。本页面描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
有关Java SE 9及后续版本中更新的语言功能的摘要,请参阅Java语言变更。
有关所有JDK版本的新功能、增强功能和已删除或弃用选项的信息,请参阅JDK发行说明。
Path
类包含各种方法,可用于获取有关路径的信息,访问路径的元素,将路径转换为其他形式或提取路径的部分。还有用于匹配路径字符串的方法以及用于删除路径中的冗余的方法。本课程将介绍这些Path
方法,有时称为语法操作,因为它们操作的是路径本身,而不是访问文件系统。
本节内容包括以下内容:
Path
实例包含用于指定文件或目录位置的信息。在定义路径时,可以提供一系列一个或多个名称。可以包含根元素或文件名,但两者都不是必需的。路径可能只包含一个目录或文件名。
您可以使用Paths
(注意复数形式)助手类中的以下get
方法之一轻松创建Path
对象:
Path p1 = Paths.get("/tmp/foo"); Path p2 = Paths.get(args[0]); Path p3 = Paths.get(URI.create("file:///Users/joe/FileTest.java"));
Paths.get
方法是以下代码的简写形式:
Path p4 = FileSystems.getDefault().getPath("/users/sally");
以下示例假设您的主目录为/u/joe
,则创建/u/joe/logs/foo.log
,如果您在Windows上,则为C:\joe\logs\foo.log
。
Path p5 = Paths.get(System.getProperty("user.home"),"logs", "foo.log");
您可以将Path
视为将这些名称元素存储为序列。目录结构中的最高元素位于索引0处。目录结构中的最低元素位于索引[n-1]
处,其中n
是Path
中名称元素的数量。可以使用这些索引检索单个元素或Path
的子序列的方法。
本课程中的示例使用以下目录结构。
以下代码片段定义了一个Path
实例,并调用了几个方法来获取有关路径的信息:
// 这些方法都不要求与Path对应的文件存在。 // Microsoft Windows语法 Path path = Paths.get("C:\\home\\joe\\foo"); // Solaris语法 Path path = Paths.get("/home/joe/foo"); System.out.format("toString: %s%n", path.toString()); System.out.format("getFileName: %s%n", path.getFileName()); System.out.format("getName(0): %s%n", path.getName(0)); System.out.format("getNameCount: %d%n", path.getNameCount()); System.out.format("subpath(0,2): %s%n", path.subpath(0,2)); System.out.format("getParent: %s%n", path.getParent()); System.out.format("getRoot: %s%n", path.getRoot());
以下是Windows和Solaris操作系统的输出结果:
调用的方法 | Solaris操作系统中的返回值 | Microsoft Windows中的返回值 | 注释 |
---|---|---|---|
toString |
/home/joe/foo |
C:\home\joe\foo |
返回Path 的字符串表示形式。如果使用Filesystems.getDefault().getPath(String) 或Paths.get (后者是getPath 的方便方法)创建了路径,则该方法将执行轻微的语法清理。例如,在UNIX操作系统中,它会将输入字符串//home/joe/foo 更正为/home/joe/foo 。 |
getFileName |
foo |
foo |
返回文件名或名称元素序列的最后一个元素。 |
getName(0) |
home |
home |
返回与指定索引对应的路径元素。第0个元素是最靠近根的路径元素。 |
getNameCount |
3 |
3 |
返回路径中的元素数量。 |
subpath(0,2) |
home/joe |
home\joe |
返回Path 的子序列(不包括根元素),由开始和结束索引指定。 |
getParent |
/home/joe |
\home\joe |
返回父目录的路径。 |
getRoot |
/ |
C:\ |
返回路径的根。 |
前面的示例展示了绝对路径的输出。在下面的示例中,指定了相对路径:
// Solaris 语法 Path path = Paths.get("sally/bar"); 或者 // Microsoft Windows 语法 Path path = Paths.get("sally\\bar");
以下是在Windows和Solaris操作系统上的输出:
方法调用 | Solaris操作系统中的返回值 | Microsoft Windows中的返回值 |
---|---|---|
toString |
sally/bar |
sally\bar |
getFileName |
bar |
bar |
getName(0) |
sally |
sally |
getNameCount |
2 |
2 |
subpath(0,1) |
sally |
sally |
getParent |
sally |
sally |
getRoot |
null |
null |
许多文件系统使用“.”符号表示当前目录,“..”表示父目录。您可能遇到一个情况,其中一个Path
包含冗余的目录信息。例如,一个服务器配置为将其日志文件保存在“/dir/logs/.
”目录中,您想从路径中删除尾部的“/.
”符号。
以下示例都包含了冗余信息:
/home/./joe/foo /home/sally/../joe/foo
normalize
方法会移除任何冗余元素,包括任何“.
”或“目录/..
”的出现。前面两个示例都规范化为/home/joe/foo
。
需要注意的是,normalize
方法在清理路径时不会检查文件系统。它是一个纯语法操作。在第二个示例中,如果sally
是一个符号链接,移除sally/..
可能导致Path
不再指向预期的文件。
要清理路径并确保结果指向正确的文件,可以使用toRealPath
方法。该方法在下一节转换路径中进行介绍。
您可以使用三种方法来转换Path
。如果您需要将路径转换为可以在浏览器中打开的字符串,可以使用toUri
方法。例如:
Path p1 = Paths.get("/home/logfile"); // 结果为 file:///home/logfile System.out.format("%s%n", p1.toUri());
toAbsolutePath
方法将路径转换为绝对路径。如果传入的路径已经是绝对路径,则返回相同的Path
对象。当处理用户输入的文件名时,toAbsolutePath
方法非常有用。例如:
public class FileTest { public static void main(String[] args) { if (args.length < 1) { System.out.println("usage: FileTest file"); System.exit(-1); } // 将输入字符串转换为Path对象。 Path inputPath = Paths.get(args[0]); // 将输入的Path转换为绝对路径。 // 通常,这意味着在当前工作目录前面添加。 // 如果使用以下方式调用此示例: // java FileTest foo // getRoot和getParent方法将在原始的"inputPath"实例上返回null。 // 在"fullPath"实例上调用getRoot和getParent将返回预期的值。 Path fullPath = inputPath.toAbsolutePath(); } }
toAbsolutePath
方法将用户输入转换为一个可查询的Path
,并返回有用的值。此方法不需要文件存在。
toRealPath
方法返回现有文件的真实路径。此方法一次执行多个操作:
true
并且文件系统支持符号链接,则此方法解析路径中的任何符号链接。Path
是相对路径,则返回绝对路径。Path
包含任何冗余元素,则返回删除了这些元素的路径。如果文件不存在或无法访问,此方法会抛出异常。您可以在想处理这些情况时捕获异常。例如:
try { Path fp = path.toRealPath(); } catch (NoSuchFileException x) { System.err.format("%s: 无此文件或目录%n", path); // 处理文件不存在的情况的逻辑。 } catch (IOException x) { System.err.format("%s%n", x); // 处理其他类型的文件错误的逻辑。 }
你可以使用resolve
方法来合并路径。你传入一个部分路径,即不包含根元素的路径,然后将该部分路径添加到原始路径后面。
例如,考虑以下代码片段:
// Solaris Path p1 = Paths.get("/home/joe/foo"); // 结果为 /home/joe/foo/bar System.out.format("%s%n", p1.resolve("bar")); or // Microsoft Windows Path p1 = Paths.get("C:\\home\\joe\\foo"); // 结果为 C:\home\joe\foo\bar System.out.format("%s%n", p1.resolve("bar"));
将绝对路径传递给resolve
方法会返回传入的路径:
// 结果为 /home/joe Paths.get("foo").resolve("/home/joe");
在编写文件I/O代码时,常见的需求是能够构建一个从文件系统中的一个位置到另一个位置的路径。你可以使用relativize
方法来实现这个需求。该方法构建了一个起始于原始路径并结束于传入路径所指定位置的路径。新路径是相对于原始路径的。
例如,考虑两个相对路径joe
和sally
:
Path p1 = Paths.get("joe"); Path p2 = Paths.get("sally");
在没有其他信息的情况下,假设joe
和sally
是兄弟节点,即位于树结构中的同一级别。要从joe
导航到sally
,你首先需要导航到父节点,然后再导航到sally
:
// 结果为 ../sally Path p1_to_p2 = p1.relativize(p2); // 结果为 ../joe Path p2_to_p1 = p2.relativize(p1);
再看一个稍微复杂一些的例子:
Path p1 = Paths.get("home"); Path p3 = Paths.get("home/sally/bar"); // 结果为 sally/bar Path p1_to_p3 = p1.relativize(p3); // 结果为 ../.. Path p3_to_p1 = p3.relativize(p1);
在这个例子中,两个路径共享相同的节点home
。要从home
导航到bar
,你需要先向下导航一级到sally
,然后再向下导航一级到bar
。从bar
导航到home
需要向上导航两级。
如果两个路径中只有一个包含根元素,则无法构建相对路径。如果两个路径都包含根元素,则能否构建相对路径取决于系统。
递归的
示例使用了Copy
relativize
和resolve
方法。
Path
类支持equals
方法,可以测试两个路径是否相等。startsWith
和endsWith
方法可以测试路径是否以特定字符串开头或结尾。这些方法很容易使用。例如:
Path path = ...; Path otherPath = ...; Path beginning = Paths.get("/home"); Path ending = Paths.get("foo"); if (path.equals(otherPath)) { // 相等的逻辑 } else if (path.startsWith(beginning)) { // 路径以“/home”开头 } else if (path.endsWith(ending)) { // 路径以“foo”结尾 }
Path
类实现了Iterable
接口。iterator
方法返回一个对象,可以遍历路径中的名称元素。返回的第一个元素是离根目录最近的元素。以下代码段遍历路径,并打印每个名称元素:
Path path = ...; for (Path name: path) { System.out.println(name); }
Path
类还实现了Comparable
接口。可以使用compareTo
方法来比较Path
对象,这在排序中很有用。
还可以将Path
对象放入Collection
中。有关此强大功能的更多信息,请参见集合教程。
当您想要验证两个Path
对象是否定位于同一文件时,可以使用isSameFile
方法,如检查两个路径是否定位于同一文件中所述。