Use Path interface to operate on file and directory paths.
Use Files class to check, read, delete, copy, move, manage metadata of a file or directory.
In the last chapter, we reviewed the classes of the java.io package.
In the first versions of Java, this package, especially the File class, provided support for file operations. However, it had some problems, like lacking functionality and limited file attribute support.
For that reason, Java 1.4 introduced the NIO (Non-blocking Input/Output) API in the package java.nio implementing new functionality like channels, buffering, and new charsets.
However, this API didn't entirely solve the problems with the java.io package, so in Java 7, the NIO.2 API was added in the java.nio.file package (actually, since this is a new package, NIO.2 is not an update to the NIO API, besides, they focus on different things).
NIO.2 provides better support for accessing files and the file system, symbolic links, interoperability, and exceptions among others.
The primary classes of java.nio.file, Path, Paths, and Files, are intended to provide an easier way to work with files and to be a replacement for the java.io.File class.
These classes will be the focus of this chapter. Let's start with Path and Paths.
In the previous chapter, we also reviewed concepts like file systems and paths.
Well, the Path interface defines an object that represents the path to a file or a directory.
When you think about the fact that a path varies between different file systems, it makes sense that Path is an interface. Thanks to this, Java transparently handles different implementations between platforms.
For example, here are some differences between Windows-based and Unix-based systems:
c:\). In Unix-based systems, it's a forward slash (/).c:\temp\file.txt). In Unix-based systems, it starts with a forward slash (like /temp/file.txt).Since Path is an interface and Java handles its implementations, we have to use a utility class to create Path instances.
java.nio.file.Paths is this class. It provides two methods to create a Path object:
static Path get(String first, String... more)
  static Path get(URI uri)
Be very careful with the names:
Path is the interface with methods to work with paths.
Paths is the class with static methods to create a Path object.
With the first version of Paths.get() you can create a Path object in these ways:
// With an absolute path in windows
  Path pathWin = Paths.get("c:\\temp\\file.txt");
  // With an absolute path in unix
  Path pathUnix = Paths.get("/temp/file.txt");
  // With a relative path
  Path pathRelative = Paths.get("file.txt");
  //Using the varargs parameter
  // (the separator is inserted automatically)
  Path pathByParts = Paths.get("c:", "temp", "file.txt");
With the second version, you have to use a java.net.URI instance. Since we're working with files, the URI schema must be file://:
try {
      Path fileURI = Paths.get(new URI("file:///c:/temp/file.txt"));
  } catch (URISyntaxException e) {
      //This checked exception is thrown by the URI constructor
  }
If you don't want to catch URISyntaxException, you can use the static method URI.create(String). It wraps the URISyntaxException exception in an IllegalArgumentException (a subclass of RuntimeException):
Path fileURI = Paths.get(URI.create("file:///c:/temp/file.txt"));
Notice the three slashes. file:/// represents an absolute path (the file:// schema plus another slash for the root directory). We can test this with the help of the toAbsolutePath() method, which returns the absolute path representation of a Path object:
Path fileURI = Paths.get(URI.create("file:///file.txt"));
  System.out.println(fileURI.toAbsolutePath());
This will print either:
C:\file.txt // in Windows-based systems
  /file.txt // Or in Unix-based systems
We can also create a Path from a File and vice-versa:
File file = new File("/file.txt");
  Path path = file.toPath();
  
  path = Paths.get("/file.txt");
  file = path.toFile();
And just to make clear that the Path instance is system-dependent, let me tell you that Paths.get() is actually equivalent to:
Path path = FileSystems.getDefault().getPath("c://temp");
As you can see from the examples, the absolute path representation of a Path object has a root component (either c:\ or /) and a sequence of names separated by a (forward or backward) slash.
These names represent the directories needed to navigate to the target file or directory. The last name in the sequence represents the name of the target file or directory.
For example, the elements of the path c:\temp\dir1\file.txt (or its Unix equivalent, /temp/dir1/file.txt) are:
Root: c:\ (or /)
  Name 1: temp
  Name 2: dir1
  Name 3: file.txt
The Path object has some methods to get this information. Except for toString() and getNameCount(), each of these methods returns a Path object):
Path path = Paths.get("C:\\temp\\dir1\\file.txt");
  // Or Path path = Paths.get("/temp/dir1/file.txt");
  System.out.println("toString(): " + path.toString());
  System.out.println("getFileName(): " + path.getFileName());
  System.out.println("getNameCount(): " +path.getNameCount());
  // Indexes start from zero 
  System.out.println("getName(0): " + path.getName(0));
  System.out.println("getName(1): " + path.getName(1));
  System.out.println("getName(2): " + path.getName(2));
  // subpath(beginIndex, endIndex) from beginIndex to endIndex-1
  System.out.println("subpath(0,2): " + path.subpath(0,2));
  System.out.println("getParent(): " + path.getParent());
  System.out.println("getRoot(): " + path.getRoot());
The output:
toString(): C:\temp\dir1\file.txt // Or /temp/dir1/file.txt
  getFileName(): file.txt
  getNameCount(): 3
  getName(0): temp
  getName(1): dir1
  getName(2): file.txt
  subpath(0,2): temp\dir1 // Or temp/dir1
  getParent(): C:\temp\dir1 // Or /temp/dir1
  getRoot(): C:\ // Or /
Passing an invalid index to getName() and subpath() will throw an IllegalArgumentException (a RuntimeException).
If the path is specified as a relative one (and assuming this code is executed from the c:\temp directory):
Path path = Paths.get("dir1\\file.txt");// Or dir1/file.txt 
  System.out.println("toString(): " + path.toString());
  System.out.println("getFileName(): " + path.getFileName());
  System.out.println("getNameCount(): " + path.getNameCount());
  System.out.println("getName(0): " + path.getName(0));
  System.out.println("getName(1): " + path.getName(1));
  System.out.println("subpath(0,2): " + path.subpath(0,2));
  System.out.println("getParent(): " + path.getParent());
  System.out.println("getRoot(): " + path.getRoot());
The output:
toString(): dir1\file.txt // Or dir1/file.txt
  getFileName(): file.txt
  getNameCount(): 2
  getName(0): dir1
  getName(1): file.txt
  subpath(0,2): dir1\file.txt // Or dir1/file.txt
  getParent(): dir1
  getRoot(): null
When working with paths, you can use:
. to refer to the current directory.. to refer to the parent directoryFor example:
// refers to /temp/file.txt
Path p1 = Paths.get("/temp/./file.txt");  
  // refers to /temp//file.txt
Path p2 = Paths.get( "/temp/dir1/../file.txt"); 
In these cases, you can use the normalize() method to remove redundancies like . and .. (in other words, to "normalize" it):
Path path = Paths.get("/temp/dir1/../file.txt");
  System.out.println(path); // /temp/dir1/../file.txt
  Path path2 = path.normalize();
  System.out.println(path2); // /temp/file.txt
This method does not access the file system to know if a file exists, so removing .. and a preceding name from a path may result in a path that no longer references the original file. This can happen when that previous name is a symbolic link (a reference to another file).
It's better to use the toRealPath() method:
Path toRealPath(LinkOption... options) throws IOException
This method does the following:
LinkOption.NOFOLLOW_LINKS is passed as an argument, symbolic links are not followed (by default it does).Path with redundant elements removed (if any).We can combine two paths. There are two cases.
First case. If we have an absolute path and we want to combine it with a second path that doesn't have a root element (a partial path), the second path is appended:
Path path = Paths.get("/temp");
  System.out.println(path.resolve("newDir")); // /temp/newDir
Second case. If we have a partial or relative path, and we want to combine it with an absolute path, this absolute path is returned:
Path path = Paths.get("newDir");
  System.out.println(path.resolve("/temp")); // /temp
relativize() is another interesting method.
path1.relativize(path2) is like saying give me a path that shows how to get from path1 to path2.
For example, if we are in directory /temp and we want to go to /temp/dir1/subdir, we have to go first to dir1 and then to subdir:
Path path1 = Paths.get("temp");
  Path path2 = Paths.get("temp/dir1/file.txt");
  Path path1ToPath2 = path1.relativize(path2); // dir1/file.txt
If the paths represent two relatives paths without any other information, they are considered siblings, so you have to go to the parent directory and then go to the other directory:
Path path1 = Paths.get("dir1");
  Path path1ToPath2 = path1.relativize(Paths.get("dir2")); // ../dir2
Notice that both examples use relative paths.
If one of the paths is an absolute path, a relative path cannot be constructed because of the lack of information and a llegalArgumentException will be thrown.
If both paths are absolute, the result is system-dependent.
Path extends the Iterable interface so you can do something like this:
Path path = Paths.get("c:\\temp\\dir1\\file.txt");
  for(Path name : path) {
      System.out.println(name);
  }
The output:
temp
  dir1
  file.txt
Path extends the Comparable interface and the equals() method to test two paths for equality.
compareTo() compares two paths lexicographically. It returns:
The equals() implementation is system-dependent (for example, it's case insensitive on Windows systems). However, it returns false if the argument is not a Path or if it belongs to a different file system.
In addition, the methods startsWith() and endsWith() both test whether a path begins or ends with some String (in this case, the methods return true only if the string represents an actual element) or Path. So given:
Path absPath = Paths.get("c:\\temp\\dir1\\file.txt");
  Path relPath = Paths.get("temp\\dir1\\file.txt");
boolean startsWith(Path other)
  absPath.startsWith(Paths.get("c:\\temp\\file.txt")); // false
  absPath.startsWith(Paths.get("c:\\temp\\dir1\\img.jpg")); // false
  absPath.startsWith(Paths.get("c:\\temp\\dir1\\")) // true
  absPath.startsWith(relPath); // false
  
  boolean startsWith(String other)
  relPath.startsWith("t"); // false
  relPath.startsWith("temp"); // true
  relPath.startsWith("temp\\d"); // false
  relPath.startsWith("temp\\dir1"); // true
  
  boolean endsWith(Path other)
  absPath.endsWith("file.txt"); // true
  absPath.endsWith("d:\\temp\\dir1\\file.txt"); // false
  relPath.endsWith(absPath); // false
  
  boolean endsWith(String other)
  relPath.endsWith("txt"); // false
  relPath.endsWith("file.txt"); // true
  relPath.endsWith("\\dir1\\file.txt"); // false
  relPath.endsWith("dir1\\file.txt"); // true
These methods don't take into account trailing separators, so if we have the Path temp/dir1, invoking, for example, endsWith() with dir1/, it returns true.
The java.nio.file.Files class has static methods for common operations on files and directories. In contrast with the java.io.File class, all methods of Files work with Path objects (so don't confuse File and Files).
For example, we can check if a path actually exists (or doesn't exist) with the methods:
static boolean exists(Path path, LinkOption... options)
  static boolean notExists(Path path, LinkOption... options)
If LinkOption.NOFOLLOW_LINKS is present, symbolic links are not followed (by default they are).
We can check if a path is readable (it's not if the file doesn't exist or if the JVM doesn't have the privileges to access it):
static boolean isReadable(Path path)
We can check if a path is writable (it's not if the file doesn't exist or if the JVM doesn't have the privileges to access it):
static boolean isWritable(Path path)
We can check if a file exists and is executable:
static boolean isExecutable(Path path)
Or even check if two paths refer to the same file (useful if one path represents a symbolic link). If both Path objects are equal then this method returns true without checking if the file exists:
static boolean isSameFile(Path path,
                            Path path2) throws IOException
To read a file, we can load the entire file into memory (only useful for small files) with the methods:
static byte[] readAllBytes(Path path)
                                throws IOException
  static List<String> readAllLines(Path path)
                                throws IOException
  static List<String> readAllLines(Path path, Charset cs)
                                throws IOException
For example:
try {
      // By default it uses StandardCharsets.UTF_8
      List<String> lines = Files.readAllLines(
                               Paths.get("file.txt"));
      lines.forEach(System.out::println); }
  } catch (IOException e) { /** */ }
Or to read a file in an efficient way:
static BufferedReader newBufferedReader(Path path)
          throws IOException
  static BufferedReader newBufferedReader(Path path, Charset cs)
          throws IOException
For example:
Path path = Paths.get("/temp/dir1/files.txt");
  // By default it uses StandardCharsets.UTF_8
  try (BufferedReader reader = Files.newBufferedReader(path,
          StandardCharsets.ISO_8859_1)) {
      String line = null;
      while((line = reader.readLine()) != null)
          System.out.println(line);
  } catch (IOException e) { /** ... */ }
The Files class has two methods to delete files/directories.
static void delete(Path path) throws IOException
It removes the file/directory or throws an exception if something fails:
try {
      Files.delete(Paths.get("/temp/dir1/file.txt"));
      Files.delete(Paths.get("/temp/dir1"));
  } catch (NoSuchFileException nsfe) {
      // If the file/directory doesn't exists
  } catch (DirectoryNotEmptyException dnee) {
      // To delete a directory, it must be empty,
      // otherwise, this exception is thrown
  } catch (IOException ioe) {
      // File permission or other problems
  }
The second method is:
static boolean deleteIfExists(Path path) throws IOException
This method returns true if the file was deleted or false if the file could not be removed because it did not exist, in other words, unlike the first method, this doesn't throw a NoSuchFileException (but it still throws a DirectoryNotEmptyException and an IOException for other problems):
try {
      Files.deleteIfExists(Paths.get("/temp/dir1/file.txt"));
  } catch (DirectoryNotEmptyException dnee) {
      // To delete a directory, it must be empty,
  } catch (IOException ioe) {
      // File permission or other problems
  }
To copy files/directories, we have the method:
static Path copy(Path source, Path target,
                   CopyOption... options) throws IOException
It returns the path to the target file, and when copying a directory, its content won't be copied.
By default, the copy fails if the destination file already exists. Also, file attributes won't be copied, and when copying a symbolic link, its target will be copied.
We can customize this behavior with the following CopyOption enums:
FileAlreadyExistsException is thrown.Here's an example:
import static java.nio.file.StandardCopyOption. REPLACE_EXISTING;
  ...
  try {
      Files.copy(Paths.get("in.txt"),
                 Paths.get("out.txt"),
                 REPLACE_EXISTING);
  } catch (IOException e) { /** ... */ }
There are methods to copy between a stream and a Path also:
static long copy(InputStream in, Path target,
                   CopyOption... options) throws IOException
Copies all bytes from an input stream to a file. By default, the copy fails if the target already exists or is a symbolic link. If the StandardCopyOption.REPLACE_EXISTING option is specified, and the target file already exists, then it is replaced if it's not a non-empty directory. If the target file exists and is a symbolic link, then the symbolic link is replaced. Actually, in Java 8, the REPLACE_EXISTING option is the only option required to be supported by this method.
static long copy(Path source,
                   OutputStream out) throws IOException
Copies all bytes from a file to an output stream.
For example:
try (InputStream in = new FileInputStream("in.csv");
          OutputStream out = new FileOutputStream("out.csv")) {
      Path path = Paths.get("/temp/in.txt");
      // Copy stream data to a file
      Files.copy(in, path);
      // Copy the file data to a stream
      Files.copy(path, out);
  } catch (IOException e) { /** ... */ }
To move or rename a file/directory, we have the method:
static Path move(Path source, Path target,
                   CopyOption... options) throws IOException
By default, this method will follow links, throw an exception if the file already exists, and not perform an atomic move.
We can customize this behavior with the following CopyOption enums:
This method can move a non-empty directory. However, if the target exists, trying to move a non-empty directory will throw a DirectoryNotEmptyException. This exception will also be thrown when trying to move a non-empty directory across a drives or partitions.
For example:
try {
      // Move or rename dir1 to dir2
      Files.move(Paths.get("c:\\temp\\dir1|"),
                 Paths.get("c:\\temp\\dir2");
  } catch (IOException e) { /** ... */ }
When talking about a file system, metadata give us information about a file or directory, like its size, permissions, creation date, etc. This information is referred as attributes, and some of them are system-dependent.
The Files class has some methods to get or set some attributes from a Path object:
static long size(Path path) throws IOException
Returns the size of a file (in bytes).
static boolean isDirectory(Path path, LinkOption... options)
Tests whether a file is a directory.
static boolean isRegularFile(Path path, LinkOption... options)
Tests whether a file is a regular file.
static boolean isSymbolicLink(Path path)
Tests whether a file is a symbolic link.
static boolean isHidden(Path path) throws IOException
Tells whether a file is considered hidden.
static FileTime getLastModifiedTime(Path path,
              LinkOption... options) throws IOException
  static Path setLastModifiedTime(Path path,
              FileTime time) throws IOException
Returns or updates a file's last modified time.
static UserPrincipal getOwner(Path path,
              LinkOption... options) throws IOException
  static Path setOwner(Path path,
              UserPrincipal owner) throws IOException
Returns or updates the owner of the file.
In methods that take an optional LinkOption.NOFOLLOW_LINKS, symbolic links are not followed (by default they are).
In the case of getLastModifiedTime() and setLastModifiedTime() the class java.nio.file.attribute.FileTime represents the value of a file's time stamp attribute.
We can create an instance of FileTime with these static methods:
static FileTime from(Instant instant)
 static FileTime from(long value, TimeUnit unit)
  static FileTime fromMillis(long value)
And from a FileTime we can get an Instant or milliseconds as long:
Instant toInstant()
  long toMillis()
For example:
try {
      Path path = Paths.get("/temp/dir1/file.txt");
      FileTime ft = Files.getLastModifiedTime(path);
      Files.setLastModifiedTime(path,
          FileTime.fromMillis(ft.toMillis + 1000)); // adds a second
  } catch (IOException e) { /** ... */ }
In the case of getOwner() and setOwner() the interface java.nio.file.attribute.UserPrincipal is an abstract representation of an identity that can be used like this:
try {
      Path path = Paths.get("/temp/dir1/file.txt");
      // FileSystems.getDefault() also gets a FileSystem object
      UserPrincipal owner = path.getFileSystem()
                  .getUserPrincipalLookupService()
                  .lookupPrincipalByName("jane");
      Files.setOwner(path, owner);
  } catch (IOException e) { /** ... */ }
These methods are useful to get or update a single attribute. But we can also get a group of related attributes by functionality or by a particular systems implementation as a view.
The three most common view classes are:
BasicFileAttributeView to support additionally a set of DOS attribute flags that are used to indicate if the file is read-only, hidden, a system file, or archived.BasicFileAttributeView with attributes supported on POSIX systems, such as Linux and Mac. Examples of these attributes are file owner, group owner, and related access permissions.You can get a file attribute view of a given type to read or update a set of attributes with the method:
static <V extends FileAttributeView> V getFileAttributeView(
              Path path, Class<V> type,LinkOption... options)
For example, BasicFileAttributeView has only one update method:
try {
     Path path = Paths.get("/temp/dir/file.txt");
     BasicFileAttributeView view =
        Files.getFileAttributeView(path,
                   BasicFileAttributeView.class);
     // Get a class with read-only attributes
     BasicFileAttributes readOnlyAttrs =
                     view.readAttributes();
     FileTime lastModifiedTime =
                     FileTime.from(Instant.now());
     FileTime lastAccessTime =
                     FileTime.from(Instant.now());
     FileTime createTime =
                     FileTime.from(Instant.now());
     //If any argument is null,
     //the corresponding value is not changed
     view.setTimes(lastModifiedTime,
                   lastAccessTime,
                   createTime);
  } catch (IOException e) { /** ... */ }
Most of the time, you'll work with the read-only versions of the file views. In this case, you can use the following method to get them directly:
static <A extends BasicFileAttributes> A
     readAttributes(Path path, Class<A> type,
                    LinkOption... options)
          throws IOException
The second parameter is the return type of the method, the class that contains the attributes to use (notice that all attributes classes extend from BasicFileAttributes because it contains attributes common to all file systems). The third argument is when you want to follow symbolic links.
Here's an example of how to access the file attributes of a file using the java.nio.file.attribute.BasicFileAttributes class:
try {
     Path path = Paths.get("/temp/dir1/file.txt");
     BasicFileAttributes attr = Files.readAttributes(
           path, BasicFileAttributes.class);
     // Size in bytes
    System.out.println("size(): " + attr.size());
     // Unique file identifier (or null if not available)
     System.out.println("fileKey(): " + attr.fileKey());
  
     System.out.println("isDirectory(): " + attr.isDirectory());
     System.out.println("isRegularFile(): " + attr.isRegularFile());
     System.out.println("isSymbolicLink(): " + attr.isSymbolicLink());
     // Is something other than a file, directory, or symbolic link?
     System.out.println("isOther(): " + attr.isOther());
  
     // The following methods return a FileTime instance
     System.out.println("creationTime(): " + attr.creationTime());
     System.out.println("lastModifiedTime():"+attr.lastModifiedTime());
     System.out.println("lastAccessTime(): " + attr.lastAccessTime());
  } catch (IOException e) { /** ... */ }
java.nio.file are Path, Paths, and Files. They are intended to be a replacement of the java.io.File class.Path interface defines an object that represents the path to a file or a directory.java.nio.file.Paths provides methods to create a Path object.Path object has a root component (either c:\ or /) and a sequence of names separated by a (forward or backward) slash.Path interface has methods to get the elements of the path, normalize paths, and get attributes of the path (isAbsolute(), getFileSystem(), etc), among others. It also extends Comparable and implements equals() to test for equality.java.nio.file.Files class has static methods for common operations on files and directories. In contrast with the java.io.File class, all methods of Files work with Path objects.isHidden()) or in a group through views.BasicFileAttributeView, DosFileAttributeView, and PosixFileAttributeView.getFileAttributeView().readAttributes().1. Given:
Path path1 = Paths.get("/projects/work/../fun");
  Path path2 = Paths.get("games");
  System.out.println(path1.resolve(path2));
Which of the following is the result of executing the above lines?
 A. /project/work/fun/games
 B. /project/fun/games
 C. /project/work/../fun/games
 D. games
2. Given:
Path path = Paths.get("c:\\Users\\mark");
Which of the following will return Users?
 A. path.getRoot()
 B. path.getName(0)
 C. path.getName(1)
 D. path.subpath(0, 0);
3. Which of the following is not a valid CopyOption for Files.copy()?
 A. NOFOLLOW_LINKS
 B. REPLACE_EXISTING
 C. ATOMIC_MOVE
 D. COPY_ATTRIBUTES
4. Given:
Path path =
    Paths.get("c:\\.\\temp\\data\\..\\.\\dir\\..\\file.txt");
  try {
     path = path.toRealPath();
  } catch (IOException e) { }
  System.out.println(path.subpath(1,2));
Which is the result?
 A. temp
 B. data
 C. dir
 D. file.txt
5. Which of the following is a valid way to set a file's create time?
A.
FileTime time = FileTime.from(Instance.now());
  Files.getFileAttributeView(path,
        BasicFileAttributeView.class)
     .setTimes(null, time, null);
B.
Files.setCreateTime(path,
     FileTime.from(Instance.now());
C.
Files.getFileAttributeView(path,
        BasicFileAttributeView.class)
     .setTimes(null, null, Instance.now());
D.
FileTime time = FileTime.from(Instance.now());
  Files.getFileAttributeView(path,
        BasicFileAttributeView.class)
     .setTimes(null, null, time);