Enhancements Added to NIO by JDK 7
Beginning with JDK 7, the NIO system was substantially expanded and enhanced. In addition to support for the try-with-resources statement (which provides automatic resource management), the improvements included three new packages (java.nio.file, java.nio.file.attribute, and java.nio.file.spi); several new classes, interfaces, and methods; and direct support for stream-based I/O. The additions have greatly expanded the ways in which NIO can be used, especially with files. Several of the key additions are described in the following sections.
The Path Interface
Perhaps the single most important addition to the NIO system is the Path interface because it encapsulates a path to a file. As you will see, Path is the glue that binds together many of the NIO.2 file-based features. It describes a file’s location within the directory structure. Path is packaged in java.nio.file, and it inherits the following interfaces: Watchable, Iterable<Path>, and Comparable<Path>. Watchable describes an object that can be monitored for changes. The Iterable and Comparable interfaces were described earlier in this book.
Path declares a number of methods that operate on the path. A sampling is shown in Table 21-3. Pay special attention to the getName( ) method. It is used to obtain an element in a path. It works using an index. At index zero is the part of the path nearest the root, which is the leftmost element in a path. Subsequent indexes specify elements to the right of the root. The number of elements in a path can be obtained by calling getNameCount( ). If you want to obtain a string representation of the entire path, simply call toString( ). Notice that you can resolve a relative path into an absolute path by using the resolve( ) method.
boolean endsWith(String path) : Returns true if the invoking Path ends with the path specified by path. Otherwise, returns false.
boolean endsWith(Path path) : Returns true if the invoking Path ends with the path specified by path. Otherwise, returns false.
Path getFileName( ) : Returns the filename associated with the invoking Path.
Path getName(int idx) : Returns a Path object that contains the name of the path element specified by idx within the invoking object. The leftmost element is at index 0. This is the element nearest the root. The rightmost element is at getNameCount( ) – 1.
int getNameCount( ) : Returns the number of elements beyond the root directory in the invoking Path.
Path getParent( ) : Returns a Path that contains the entire path except for the name of the file specified by the invoking Path.
Path getRoot( ) : Returns the root of the invoking Path.
boolean isAbsolute( ) : Returns true if the invoking Path is absolute. Otherwise, returns false.
Path resolve(Path path) : If path is absolute, path is returned. Otherwise, if path does not contain a root, path is prefixed by the root specified by the invoking Path and the result is returned. If path is empty, the invoking Path is returned. Otherwise, the behavior is unspecified.
Path resolve(String path) : If path is absolute, path is returned. Otherwise, if path does not contain a root, path is prefixed by the root specified by the invoking Path and the result is returned. If path is empty, the invoking Path is returned. Otherwise, the behavior is unspecified.
boolean startsWith(String path) : Returns true if the invoking Path starts with the path specified by path. Otherwise, returns false.
boolean startsWith(Path path) : Returns true if the invoking Path starts with the path specified by path. Otherwise, returns false.
Path toAbsolutePath( ) : Returns the invoking Path as an absolute path.
String toString( ) : Returns a string representation of the invoking Path.
Table 21-3 A Sampling of Methods Specified by Path
One other point: When updating legacy code that uses the File class defined by java.io, it is possible to convert a File instance into a Path instance by calling toPath( ) on the File object. Furthermore, it is possible to obtain a File instance by calling the toFile( ) method defined by Path.
The Files Class
Many of the actions that you perform on a file are provided by static methods within the Files class. The file to be acted upon is specified by its Path. Thus, the Files methods use a Path to specify the file that is being operated upon. Files contains a wide array of functionality. For example, it has methods that let you open or create a file that has the specified path. You can obtain information about a Path, such as whether it is executable, hidden, or read-only. Files also supplies methods that let you copy or move files. A sampling is shown in Table 21-4. In addition to IOException, several other exceptions are possible. JDK 8 adds these four methods to Files: list( ), walk( ), lines( ), and find( ). All return a Stream object. These methods help integrate NIO with the new stream API defined by JDK 8 and described in Chapter 29.
Notice that several of the methods in Table 21-4 take an argument of type OpenOption. This is an interface that describes how to open a file. It is implemented by the StandardOpenOption class, which defines an enumeration that has the values shown in Table 21-5.
Value : Meaning
APPEND : Causes output to be written to the end of the file.
CREATE : Creates the file if it does not already exist.
CREATE_NEW : Creates the file only if it does not already exist.
DELETE_ON_CLOSE : Deletes the file when it is closed.
DSYNC : Causes changes to the file to be immediately written to the physical file. Normally, changes to a file are buffered by the file system in the interest of efficiency, being written to the file only as needed.
READ : Opens the file for input operations.
SPARSE : Indicates to the file system that the file is sparse, meaning that it may not be completely filled with data. If the file system does not support sparse files, this option is ignored.
SYNC : Causes changes to the file or its metadata to be immediately written to the physical file. Normally, changes to a file are buffered by the file system in the interest of efficiency, being written to the file only as needed.
TRUNCATE_EXISTING : Causes a preexisting file opened for output to be reduced to zero length.
WRITE : Opens the file for output operations.
Table 21-5 The Standard Open Options
The Paths Class
Because Path is an interface, not a class, you can’t create an instance of Path directly through the use of a constructor. Instead, you obtain a Path by a calling a method that returns one. Frequently, you do this by using the get( ) method defined by the Paths class. There are two forms of get( ). The one used in this chapter is shown here:
static Path get(String pathname, String ... parts)
It returns a Path that encapsulates the specified path. The path can be specified in two ways. First, if parts is not used, then the path must be specified in its entirety by pathname. Alternatively, you can pass the path in pieces, with the first part passed in pathname and the subsequent elements specified by the parts varargs parameter. In either case, if the path specified is syntactically invalid, get( ) will throw an InvalidPathException.
The second form of get( ) creates a Path from a URI. It is shown here: static Path get(URI uri)
The Path corresponding to uri is returned.
It is important to understand that creating a Path to a file does not open or create a file. It simply creates an object that encapsulates the file’s directory path.
The File Attribute Interfaces
Associated with a file is a set of attributes. These attributes include such things as the file’s time of creation, the time of its last modification, whether the file is a directory, and its size. NIO organizes file attributes into several different interfaces. Attributes are represented by a hierarchy of interfaces defined in java.nio.file.attribute. At the top is BasicFileAttributes. It encapsulates the set of attributes that are commonly found in a variety of file systems. The methods defined by BasicFileAttributes are shown in Table 21-6.
Method : Description
FileTime creationTime( ) : Returns the time at which the file was created. If creation time is not provided by the file system, then an implementation-dependent value is returned.
Object fileKey( ) : Returns the file key. If not supported, null is returned.
boolean isDirectory( ) : Returns true if the file represents a directory.
boolean isOther( ) : Returns true if the file is not a file, symbolic link, or a directory.
boolean isRegularFile( ) : Returns true if the file is a normal file, rather than a directory or symbolic link.
boolean isSymbolicLink( ) : Returns true if the file is a symbolic link.
FileTime lastAccessTime( ) : Returns the time at which the file was last accessed. If the time of last access is not provided by the file system, then an implementation- dependent value is returned.
FileTime lastModifiedTime( ) : Returns the time at which the file was last modified. If the time of last modification is not provided by the file system, then an implementation-dependent value is returned.
long size( ) : Returns the size of the file.
Table 21-6 The Methods Defined by BasicFileAttributes
From BasicFileAttributes two interfaces are derived: DosFileAttributes and
PosixFileAttributes. DosFileAttributes describes those attributes related to the FAT file system as first defined by DOS. It defines the methods shown here:
Method : Description
boolean isArchive( ) : Returns true if the file is flagged for archiving and false otherwise.
boolean isHidden( ) : Returns true if the file is hidden and false otherwise.
boolean isReadOnly( ) : Returns true if the file is read-only and false otherwise.
boolean isSystem( ) : Returns true if the file is flagged as a system file and false otherwise.
PosixFileAttributes encapsulates attributes defined by the POSIX standards. (POSIX stands for Portable Operating System Interface.) It defines the methods shown here:
GroupPrincipal group( ) : Returns the file’s group owner.
UserPrincipal owner( ) : Returns the file’s owner.
Set<PosixFilePermission> permissions( ) : Returns the file’s permissions.
There are various ways to access a file’s attributes. First, you can obtain an object that encapsulates a file’s attributes by calling readAttributes( ), which is a static method defined by Files. One of its forms is shown here:
static <A extends BasicFileAttributes>
A readAttributes(Path path, Class<A> attrType, LinkOption... opts) throws IOException
This method returns a reference to an object that specifies the attributes associated with the file passed in path. The specific type of attributes is specified as a Class object in the attrType parameter. For example, to obtain the basic file attributes, pass BasicFileAttributes.class to attrType. For DOS attributes, use DosFileAttributes.class, and for POSIX attributes, use PosixFileAttributes.class. Optional link options are passed via opts. If not specified, symbolic links are followed. The method returns a reference to requested attributes. If the requested attribute type is not available, UnsupportedOperationException is thrown. Using the object returned, you can access the file’s attributes.
A second way to gain access to a file’s attributes is to call getFileAttributeView( ) defined by Files. NIO defines several attribute view interfaces, including AttributeView,
BasicFileAttributeView, DosFileAttributeView, and PosixFileAttributeView, among others. Although we won’t be using attribute views in this chapter, they are a feature that you may find helpful in some situations.
In some cases, you won’t need to use the file attribute interfaces directly because the Files class offers static convenience methods that access several of the attributes. For example,
Files includes methods such as isHidden( ) and isWritable( ).
It is important to understand that not all file systems support all possible attributes. For example, the DOS file attributes apply to the older FAT file system as first defined by DOS. The attributes that will apply to a wide variety of file systems are described by BasicFileAttributes. For this reason, these attributes are used in the examples in this chapter.
The FileSystem, FileSystems, and FileStore Classes
You can easily access the file system through the FileSystem and FileSystems classes packaged in java.nio.file. In fact, by using the newFileSystem( ) method defined by FileSystems, it is even possible to obtain a new file system. The FileStore class encapsulates the file storage system. Although these classes are not used directly in this chapter, you may find them helpful in your own applications.