Chapter 6. Java File I/O (NIO.2)

6.1.  Use the Path class to operate on file and directory paths

[Note]

A java.nio.file.Path object is somewhat analogous to a java.io.File object as it can represent a file or directory on the file system. A Path object is more abstract though, in that it is a sequence of names that represent a directory hierarchy (that may or may not include a file) on the file system. There are no methods in the Path interface that allow for working with directories or files. The methods defined are for working with or manipulating Path objects only, resolving one Path to another. (There is one method that can be used to obtain a java.io.File object from a java.nio.file.Path, toFile. Likewise the java.io.File class now contains a toPath method.) To work with files and directories, Path objects are used in conjunction with the java.nio.file.Files class. The java.nio.file.Files class consists entirely of static methods for manipulating directories and files, including copy, move and functions for working with symbolic links.

Implementations of java.nio.file.Path interface are immutable and safe for use by multiple concurrent threads.

In Java 6 and earlier you do that:

File file = new File("README.TXT");
					

In Java 7 you do that:

// Paths class consists exclusively of static methods that return a Path by
// converting a path string or URI

Path path = Paths.get("README.TXT");
					

To make the migration to Java 7 easier, the File class has a new method toPath() that allows you to transform File to Path:

Path path = new File("README.TXT").toPath();
					

Like File, a Path can also refer to a not existing file. That is only file path, NOT the data containing in a file.

The following code snippet defines a Path instance and then invoke several methods to obtain information about the path. Assume we have following folders structure on Windows platform:

C:\home\zaikin\foo
					

  • Create Path:

    Path path = Paths.get("C:\\home\\zaikin\\foo");
    								

    NOTE: None of listed here methods requires that the file corresponding to the Path existed.

  • String s = path.toString();
    								

    returns

    C:\home\zaikin\foo
    								

    The toString() method returns the string representation of the Path. If the path was created using Filesystems.getDefault().getPath(String) or Paths.get(...) (the latter is a convenience method for getPath), the method performs minor syntactic cleanup. For example, in a UNIX operating system, it will correct the input string //home/zaikin/foo to /home/zaikin/foo.

  • Path p = path.getFileName()
    								

    returns

    foo
    								

    The getFileName() method returns the name of the file or directory denoted by this path as a Path object. The file name is the farthest element from the root in the directory hierarchy (last element of the sequence of name elements).

  • Path p = path.getName(0)
    								

    returns

    home
    								

    The getName(int index) method returns a name element of this path as a Path object.

    The index parameter is the index of the name element to return. The element that is closest to the root in the directory hierarchy has index 0. The element that is farthest from the root has index count-1.

    NOTE: if index is negative, index is greater than or equal to the number of elements, or this path has zero name elements, then IllegalArgumentException is thrown.

  • int i = path.getNameCount();
    								

    returns

    3
    								

    The getNameCount() method returns the number of name elements in the path.

  • Path p = path.subpath(0,2);
    								

    returns

    home\zaikin
    								

    The subpath(int beginIndex, int endIndex) method returns a relative Path that is a subsequence of the name elements of this path.

    The beginIndex and endIndex parameters specify the subsequence of name elements. The name that is closest to the root in the directory hierarchy has index 0. The name that is farthest from the root has index count-1. The returned Path object has the name elements that begin at beginIndex and extend to the element at index endIndex-1.

  • Path p = path.getRoot();
    								

    returns

    C:\
    								

    The getRoot() method returns the root component of this path as a Path object, or null if this path does not have a root component (e.g. for relative paths).

    For UNIX platform the root will be "/". For Windows, something like "C:\".

  • Path p = path.resolveSibling("bar");
    								

    returns

    C:\home\zaikin\bar
    								

    The resolveSibling(Path other) and resolveSibling(String other) methods resolve the given path against this path's parent path. This is useful where a file name needs to be replaced with another file name. For example, suppose that the name separator is "/" and a path represents "dir1/dir2/foo", then invoking this method with the Path "bar" will result in the Path "dir1/dir2/bar". If this path does not have a parent path, or other is absolute, then this method returns other. If other is an empty path then this method returns this path's parent, or where this path doesn't have a parent, the empty path.

Removing redundancies from a Path

Given the following code:

Path path = Paths.get("C:\\home\\zaikin\\..\\..\\foo");
Path p = path.normalize();
System.out.println(p);
System.out.println(p.getNameCount());
					

it prints (NOTE: implementations of Path interface are immutable by design)

C:\foo
5
					

The normalize() method returns a path that is this path with redundant name elements eliminated.

The precise definition of this method is implementation dependent but in general it derives from this path, a path that does not contain redundant name elements. In many file systems, the "." and ".." are special names used to indicate the current directory and parent directory. In such file systems all occurrences of "." are considered redundant. If a ".." is preceded by a non-".." name then both names are considered redundant (the process to identify such names is repeated until is it no longer applicable).

NOTE: This method does not access the file system; the path may not locate a file that exists. Eliminating ".." and a preceding name from a path may result in the path that locates a different file than the original path. This can arise when the preceding name is a symbolic link.

Creating a path between two paths

A common requirement when you are writing file I/O code is the capability to construct a path from one location in the file system to another location. You can meet this using the relativize() method. This method constructs a path originating from the original path and ending at the location specified by the passed-in path. The new path is relative to the original path. Relativization is the inverse of resolution.

For example, consider two relative paths:

Path p1 = Paths.get("home");
Path p2 = Paths.get("home/zaikin/foo");
					

the folowing code:

Path p1_p2 = p1.relativize(p2);
System.out.println(p1_p2);

Path p2_p1 = p2.relativize(p1);
System.out.println(p2_p1);
					

will produce the following output:

zaikin\foo
..\..
					

In this example, the two paths share the same node, home. To navigate from home to foo, you first navigate one level down to zaikin and then one more level down to foo. Navigating from foo to home requires moving up two levels.

A relative path CANNOT be constructed if only one of the paths includes a root element. If both paths include a root element, the capability to construct a relative path is system dependent.

Joining two Paths

You can combine paths by using the Path.resolve(Path other) and Path.resolve(String other) methods. You pass in a partial path, which is a path that does not include a root element, and that partial path is appended to the original path.

If the other parameter is an absolute path then this method trivially returns other. If other is an empty path then this method trivially returns this path. Otherwise this method considers this path to be a directory and resolves the given path against this path. In the simplest case, the given path does not have a root component, in which case this method joins the given path to this path and returns a resulting path that ends with the given path. Where the given path has a root component then resolution is highly implementation dependent and therefore unspecified.

For example, consider the following code snippet:

Path p3 = Paths.get("C:\\home\\zaikin\\foo");
System.out.format("%s%n", p3.resolve("bar"));
					

the result is:

C:\home\zaikin\foo\bar
					

Passing an absolute path to the resolve method returns the passed-in path:

Path p4 = Paths.get("bar");
System.out.format("%s%n", p4.resolve("C:\\home\\zaikin\\foo"));
					

the result is:

C:\home\zaikin\foo
					

Professional hosting         Free 'Oracle Certified Expert Web Services Developer 6' Guide     Free SCDJWS 5.0 Guide