Java NIO2 Path API – Java NIO2路径API

最后修改: 2016年 11月 3日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we will learn how to use the new I/O (NIO2) Path API in Java.

在这篇文章中,我们将学习如何在Java中使用新的I/O(NIO2)Path API。

The Path APIs in NIO2 constitute one of the major new functional areas that shipped with Java 7 and specifically a subset of the new file system API alongside File APIs.

NIO2中的Path API构成了与Java 7一起发货的主要新功能区之一,特别是与File APIs一起的新文件系统API的一个子集。

2. Setup

2.设置

The NIO2 support is bundled in the java.nio.file package. So setting up your project to use the Path APIs is just a matter of importing everything in this package:

NIO2支持被捆绑在java.nio.file包中。因此,设置你的项目以使用Path API,只需导入该包中的所有内容即可。

import java.nio.file.*;

Since the code samples in this article will probably be running in different environments, let’s get a handle on the home directory of the user:

由于本文中的代码样本可能会在不同的环境中运行,让我们掌握一下用户的主目录。

private static String HOME = System.getProperty("user.home");

This variable will point to a valid location in any environment.

这个变量将指向任何环境中的一个有效位置。

The Paths class is the main entry point to all operations involving file system paths. It allows us to create and manipulate paths to files and directories.

Paths类是所有涉及文件系统路径操作的主要入口。它允许我们创建和操作文件和目录的路径。

Worthy of note is that path operations are mainly syntactic in nature; they have no effect on the underlying file system and neither does the file system have any effect on whether they succeed or fail. This means that passing an inexistent path as a parameter of a path operation has no bearing on whether it succeed or fails.

值得注意的是,路径操作主要是语法性的;它们对底层文件系统没有影响,文件系统对它们的成功或失败也没有影响。这意味着,把一个不存在的路径作为路径操作的参数,对其成功或失败没有影响。

3. Path Operations

3.路径操作

In this section, we will introduce the main syntax used in path operations. As its name implies, the Path class is a programmatic representation of a path in the file system.

在本节中,我们将介绍路径操作中使用的主要语法。顾名思义,Path类是文件系统中路径的一种程序化表示。

A Path object contains the file name and directory list used to construct the path and is used to examine, locate, and manipulate files.

Path对象包含用于构建路径的文件名和目录列表,用于检查、定位和操作文件。

The helper class, java.nio.file.Paths (in plural form) is the formal way of creating Path objects. It has two static methods for creating a Path from a path string:

辅助类,java.nio.file.Paths(复数形式)是创建Path对象的正式方式。它有两个静态方法用于从路径字符串中创建一个Path

Path path = Paths.get("path string");

Whether we use a forward or backslash in the path String, does not matter, the API resolves this parameter according to the underlying file system’s requirements.

我们在路径String,中使用正斜杠还是反斜杠并不重要,API会根据底层文件系统的要求来解析这个参数。

And from a java.net.URI object:

而从一个java.net.URI对象。

Path path = Paths.get(URI object);

We can now go ahead and see these in action.

我们现在可以去看看这些行动了。

4. Creating a Path

4.创建一个路径

To create a Path object from a path string:

从一个路径字符串创建一个Path对象。

@Test
public void givenPathString_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles/baeldung");
 
    assertEquals("\\articles\\baeldung", p.toString());
}

The get API can take a variable arguments parameter of path string parts (in this case, articles and baeldung) in addition to the first part (in this case, articles).

get API除了第一部分(本例中为articlesbaeldung)外,还可以接受路径字符串部分(本例中为articles)的可变参数参数。

If we provide these parts instead of a complete path string, they will be used to construct the Path object, we do not need to include the name-separators (slashes) in the variable arguments part:

如果我们提供这些部分而不是完整的路径字符串,它们将被用来构造Path对象,我们不需要在变量参数部分包含名称分隔符(斜线)。

@Test
public void givenPathParts_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles", "baeldung");
    
    assertEquals("\\articles\\baeldung", p.toString());
}

5. Retrieving Path Information

5.检索路径信息

You can think of the Path object as name elements as a sequence. A path String such as E:\baeldung\articles\java consists of three name elements i.e. baeldung, articles, and java. The highest element in the directory structure would be located at index 0, in this case being baeldung.

你可以把路径对象看作是名称元素的序列。一个路径StringE:\baeldung\articles\java由三个名称元素组成,即baeldungarticlesjava。目录结构中最高的元素将位于索引0,在这种情况下是baeldung

The lowest element in the directory structure would be located at index [n-1], where n is the number of name elements in the path. This lowest element is called the file name regardless of whether it is an actual file or not:

目录结构中最低的元素将位于索引[n-1],其中n是路径中名称元素的数量。这个最低的元素被称为文件名,不管它是否是一个实际的文件。

@Test
public void givenPath_whenRetrievesFileName_thenCorrect() {
    Path p = Paths.get("/articles/baeldung/logs");

    Path fileName = p.getFileName();
 
    assertEquals("logs", fileName.toString());
}

Methods are available for retrieving individual elements by index:

有一些方法可用于按索引检索单个元素。

@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());
}

or a sub-sequence of the path using these index ranges:

或使用这些索引范围的路径的一个子序列。

@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());
}

Each path is associated with a parent path or null if the path has no parent. The parent of a path object consists of the path’s root component, if any, and each element in the path except for the file name. As an example, the parent path of /a/b/c is /a/b and that of /a is null:

每个路径都与一个父路径相关联,如果路径没有父路径,则null。路径对象的父级由路径的根组件(如果有的话)和路径中除文件名以外的每个元素组成。例如,/a/b/c的父路径是/a/b/a的父路径是空。

@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);
}

We can also get the root element of a path:

我们还可以得到一个路径的根元素。

@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. Normalizing a Path

6.规范化的路径

Many file systems use “.” notation to denote the current directory and “..” to denote the parent directory. You might have a situation where a path contains redundant directory information.

许多文件系统使用“.”符号表示当前目录,“.”表示父目录。你可能会遇到这样的情况:一个路径包含多余的目录信息。

For example, consider the following path strings:

例如,考虑以下路径字符串。

/baeldung/./articles
/baeldung/authors/../articles
/baeldung/articles

They all resolve to the same location /baeldung/articles. The first two have redundancies while the last one does not.

它们都解析到同一个位置/baeldung/articles。前两个有冗余,而最后一个则没有。

Normalizing a path involves removing redundancies in it. The Path.normalize() operation is provided for this purpose.

规范化一个路径涉及到去除其中的冗余部分。Path.normalize()操作就是为了这个目的。

This example should now be self-explanatory:

这个例子现在应该是不言自明的了。

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect1() {
    Path p = Paths.get("/home/./baeldung/articles");

    Path cleanPath = p.normalize();
 
    assertEquals("\\home\\baeldung\\articles", cleanPath.toString());
}

This one too:

这个也是。

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect2() {
    Path p = Paths.get("/home/baeldung/../articles");

    Path cleanPath = p.normalize();
 
    assertEquals("\\home\\articles", cleanPath.toString());
}

7. Path Conversion

7.路径转换

There are operations to convert a path to a chosen presentation format. To convert any path into a string that can be opened from the browser, we use the toUri method:

有一些操作可以将路径转换为所选择的演示格式。为了将任何路径转换成可以从浏览器打开的字符串,我们使用toUri方法。

@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());
}

We can also convert a path to its absolute representation. The toAbsolutePath method resolves a path against a file system default directory:

我们也可以将一个路径转换为其绝对值。toAbsolutePath方法针对文件系统的默认目录解析路径。

@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());
}

However, when the path to be resolved is detected to be already absolute, the method returns it as is:

然而,当检测到要解决的路径已经是绝对值时,该方法会原封不动地返回它。

@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());
}

We can also convert any path to its real equivalent by calling the toRealPath method. This method tries to resolve the path by mapping it’s elements to actual directories and files in the file system.

我们也可以通过调用toRealPath方法将任何路径转换为实际等价物。这个方法试图通过将路径的元素映射到文件系统中的实际目录和文件来解决这个问题。

Time to use the variable we created in the Setup section which points to logged-in user’s home location in the file system:

是时候使用我们在Setup部分创建的变量了,它指向文件系统中登录用户的主页位置。

@Test
public void givenExistingPath_whenGetsRealPathToFile_thenCorrect() {
    Path p = Paths.get(HOME);

    Path realPath = p.toRealPath();
 
    assertEquals(HOME, realPath.toString());
}

The above test does not really tell us much about the behavior of this operation. The most obvious result is that if the path does not exist in the file system, then the operation will throw an IOException, read on.

上面的测试并没有真正告诉我们关于这个操作的行为。最明显的结果是,如果文件系统中的路径不存在,那么该操作将抛出一个IOException,继续阅读。

For the lack of a better way to drive this point home, just take a look at the next test, which attempts to convert an inexistent path to a real path:

由于没有更好的方法来说明这一点,请看下一个测试,它试图将一个不存在的路径转换为一个真正的路径。

@Test(expected = NoSuchFileException.class)
public void givenInExistentPath_whenFailsToConvert_thenCorrect() {
    Path p = Paths.get("E:\\home\\baeldung\\articles.html");
    
    p.toRealPath();
}

The test succeeds when we catch an IOException. The actual subclass of IOException that this operation throws is NoSuchFileException.

当我们捕捉到一个IOException时,测试就成功了。这个操作抛出的IOException的实际子类是NoSuchFileException

8. Joining Paths

8.连接路径

Joining any two paths can be achieved using the resolve method.

使用resolve方法可以实现任何两条路径的连接。

Simply put, we can call the resolve method on any Path and pass in a partial path as the argument. That partial path is appended to the original path:

简单地说,我们可以在任何Path上调用resolve方法,并传入一个partial path作为参数。该部分路径被追加到原始路径中。

@Test
public void givenTwoPaths_whenJoinsAndResolves_thenCorrect() {
    Path p = Paths.get("/baeldung/articles");

    Path p2 = p.resolve("java");
 
    assertEquals("\\baeldung\\articles\\java", p2.toString());
}

However, when the path string passed to the resolve method is not a partial path; most notably an absolute path, then the passed-in path is returned:

然而,当传递给resolve方法的路径字符串不是一个部分路径;最明显的是一个绝对路径,那么传入的路径就会返回。

@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());
}

The same thing happens with any path that has a root element. The path string “java” has no root element while the path string “/java” has a root element. Therefore, when you pass in a path with a root element, it is returned as is:

同样的事情也发生在任何有根元素的路径上。路径字符串“java”没有根元素,而路径字符串“/java”有一个根元素。因此,当你传入一个有根元素的路径时,它会被原样返回。

@Test
public void givenPathWithRoot_whenResolutionRetainsIt_thenCorrect2() {
    Path p = Paths.get("/baeldung/articles");

    Path p2 = p.resolve("/java");
 
    assertEquals("\\java", p2.toString());
}

9. Relativizing Paths

9.相对化路径

The term relativizing simply means creating a direct path between two known paths. For instance, if we have a directory /baeldung and inside it, we have two other directories such that /baeldung/authors and /baeldung/articles are valid paths.

术语relativizing仅仅意味着在两个已知路径之间创建一个直接路径。例如,如果我们有一个目录/baeldung,在它里面,我们有另外两个目录,这样/baeldung/authors/baeldung/articles是有效的路径。

The path to articles relative to authors would be described as “move one level up in the directory hierarchy then into articles directory” or ..\articles:

相对于authorsarticles的路径将被描述为“在目录层次结构中向上移动一级,然后进入articles目录”…/articles:

@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());
}

Assuming we move the articles directory to authors folder such that they are no longer siblings. The following relativizing operations involve creating a path between baeldung and articles and vice versa:

假设我们把articles目录移到authors文件夹,使它们不再是兄弟姐妹关系。下面的相对化操作涉及在baeldungarticles之间创建一个路径,反之亦然。

@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. Comparing Paths

10 比较路径

The Path class has an intuitive implementation of the equals method which enables us to compare two paths for equality:

Path类有一个直观的equals方法的实现,使我们能够比较两个路径是否相等。

@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));
}

You can also check if a path begins with a given string:

你也可以检查一个路径是否以一个给定的字符串开始。

@Test
public void givenPath_whenInspectsStart_thenCorrect() {
    Path p1 = Paths.get("/baeldung/articles");
 
    assertTrue(p1.startsWith("/baeldung"));
}

Or ends with some other string:

或者以其他一些字符串结束。

@Test
public void givenPath_whenInspectsEnd_thenCorrect() {
    Path p1 = Paths.get("/baeldung/articles");
  
    assertTrue(p1.endsWith("articles"));
}

11. Conclusion

11.结论

In this article, we showed Path operations in the new file system API (NIO2) that was shipped as a part of Java 7 and saw most of them in action.

在这篇文章中,我们展示了新的文件系统API(NIO2)中的Path操作,它是作为Java 7的一部分交付的,并看到了它们中的大多数在运行。

The code samples used in this article can be found in the article’s Github project.

本文中使用的代码样本可以在文章的Github项目中找到。