Java NIO2 Path API – Java NIO2路径API

最后修改: 2016年 11月 3日

1. Overview


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


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.


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


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.


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


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:


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.


And from a object:


Path path = Paths.get(URI object);

We can now go ahead and see these in action.


4. Creating a Path


To create a Path object from a path string:


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:


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

5. Retrieving Path Information


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.


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:


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:


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:


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:


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:


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


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:



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


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


This example should now be self-explanatory:


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:


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

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

7. Path Conversion


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:


public void givenPath_whenConvertsToBrowseablePath_thenCorrect() {
    Path p = Paths.get("/home/baeldung/articles.html");

    URI uri = p.toUri();

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


public void givenPath_whenConvertsToAbsolutePath_thenCorrect() {
    Path p = Paths.get("/home/baeldung/articles.html");

    Path absPath = p.toAbsolutePath();

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


public void givenAbsolutePath_whenRetainsAsAbsolute_thenCorrect() {
    Path p = Paths.get("E:\\home\\baeldung\\articles.html");

    Path absPath = p.toAbsolutePath();

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.


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


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.


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");

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


8. Joining Paths


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


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作为参数。该部分路径被追加到原始路径中。

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:


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:


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

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

9. Relativizing Paths


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.


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:


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:


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


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


public void givenTwoPaths_whenTestsEquality_thenCorrect() {
    Path p1 = Paths.get("/baeldung/articles");
    Path p2 = Paths.get("/baeldung/articles");
    Path p3 = Paths.get("/baeldung/authors");


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


public void givenPath_whenInspectsStart_thenCorrect() {
    Path p1 = Paths.get("/baeldung/articles");

Or ends with some other string:


public void givenPath_whenInspectsEnd_thenCorrect() {
    Path p1 = Paths.get("/baeldung/articles");

11. Conclusion


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.