Part 2 of a 3-part series on NIO.2 presents path-related recipes, file/directory-testing recipes, and attribute-oriented recipes My previous post launched a three-part series on NIO.2 recipes. I presented three recipes for copying files, deleting files and directories, and moving files. In this post, I present path-related recipes (such as obtaining paths and retrieving path information), file/directory-testing recipes (such as testing file/directory existence), and attribute-oriented recipes. Obtaining paths Q: How do I obtain a java.nio.file.Path object? A: You obtain a Path object, which locates a file or directory in a file system via a sequence of name elements, by calling either of the following static methods in the java.nio.file.Paths class: Path get(String first, String... more): Convert a path string, or a sequence of strings that when joined form a path string, to a Path object. This method throws java.nio.file.InvalidPathException when the path string cannot be converted to a Path because the path string contains invalid characters, or the path string is invalid for other file system-specific reasons. The Path object is obtained by invoking the default java.nio.file.FileSystem instance’s Path getPath(String first, String... more) method. Path get(URI uri): Convert the path uniform resource identifier (URI) argument to a Path object. This method throws java.lang.IllegalArgumentException when preconditions on uri don’t hold (e.g., uri must include a scheme component); java.nio.file.FileSystemNotFoundException when the uri-identified file system doesn’t exist and cannot be created, or the provider identified by the URI’s scheme component isn’t installed; and java.lang.SecurityException when any installed security manager denies an unspecified permission to access the file system. The Path object is obtained by iterating over the installed java.nio.file.spi.FileSystemProvider instances for a provider that supports the URI’s scheme, and then invoking the provider’s Path getPath(URI uri) method. The first get() method lets you compose a path out of an arbitrary number of components; for example, Path path = Paths.get("a", "b");. This method places an appropriate file system-specific separator character between these components when you convert it to a string. For example, System.out.println(path); results in ab being output on a Windows platform. You can hard-code a separator character between components, as in Path path = Paths.get("a", "", "b");. However, this isn’t a good idea. On a Windows platform, you would end up with ab. However, on a platform where the colon (:) is used as a separator, you would most likely observe InvalidPathException, pointing out that the backslash character is illegal. You might decide to use File.separator in place of a hard-coded separator character. However, this isn’t a good idea either. NIO.2 supports multiple file systems, but File.separator is associated with the default file system. Attempting to obtain a path containing File.separator in the context of a file system that doesn’t recognize this constant’s separator character would probably result in InvalidPathException. Instead of hard-coding a separator character or specifying File.separator, use FileSystem‘s String getSeparator() method. getSeparator() returns the separator character for the file system associated with the invoking FileSystem instance. For example, FileSystem.getDefault().getSeparator() returns the default file system’s separator character. I briefly demonstrated the first get() method in Part 1 of this series. Listing 1 presents the source code to another small application that reinforces what I’ve said about separator characters. Listing 1. ObtainPath.java (version 1) import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; public class ObtainPath { public static void main(String[] args) { Path path = Paths.get("a", "b"); System.out.println(path); path = Paths.get(FileSystems.getDefault().getSeparator() + "a", "b", "c"); System.out.println(path); path = Paths.get("a", ":", "b"); System.out.println(path); } } The second example shows that the separator character is also used to signify the root directory, but there is a better way to get this directory, which I’ll show later in this post. Compile Listing 1 (javac ObtainPath.java) and run the application (java ObtainPath). On a Windows platform, I observe the following output: ab abc Exception in thread "main" java.nio.file.InvalidPathException: Illegal char <:> at index 2: a:b at sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182) at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153) at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77) at sun.nio.fs.WindowsPath.parse(WindowsPath.java:94) at sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:255) at java.nio.file.Paths.get(Paths.java:84) at ObtainPath.main(ObtainPath.java:15) By the way, what happens when you pass null as one of get()‘s path components; for example, path = Paths.get("a", null, "b");? The second get() method lets you convert a URI to a path. Listing 2 presents a small application that demonstrates a couple of the problems to watch out for when using this method. Listing 2. ObtainPath.java (version 2) import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileSystemNotFoundException; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; public class ObtainPath { public static void main(String[] args) throws URISyntaxException { try { Path path = Paths.get(new URI("")); System.out.println(path); } catch (IllegalArgumentException iae) { System.err.println("You cannot pass a URI object initialized to the " + "empty string to get()."); iae.printStackTrace(); } try { Path path = Paths.get(new URI("nntp://x")); System.out.println(path); } catch (FileSystemNotFoundException fsnfe) { System.err.println("No file system associated with the specified scheme."); fsnfe.printStackTrace(); } } } Compile Listing 2 and run the application. You should observe the following exceptional output: You cannot pass a URI object initialized to the empty string to get(). java.lang.IllegalArgumentException: Missing scheme at java.nio.file.Paths.get(Paths.java:134) at ObtainPath.main(ObtainPath.java:15) No file system associated with the specified scheme. java.nio.file.FileSystemNotFoundException: Provider "nntp" not installed at java.nio.file.Paths.get(Paths.java:147) at ObtainPath.main(ObtainPath.java:27) The first example demonstrates IllegalArgumentException from a missing URI scheme component. The second example demonstrates FileSystemNotFoundException because no provider exists for the nntp scheme. Retrieving path information Q: What kinds of information can I retrieve from a Path object? A: The Path interface declares several methods for retrieving the individual name elements and other kinds of information from a Path object. The following list describes these methods: Path getFileName(): Return the name of the file or directory (the farthest element from the root in the directory hierarchy) denoted by this path as a Path object, or return null when the path has no elements. Path getName(int index): Return the name element located at index, which must range from 0 (the closest element to the root) to one less than getNameCount()‘s value. This method throws IllegalArgumentException when index is negative, or greater than or equal to the number of elements in the path — or the path has zero elements. int getNameCount(): Return the number of elements in the path. Zero is returned when the path represents a root directory only. Path getParent(): Return the parent path or null when this path doesn’t have a parent. The parent path consists of this path’s root component (if any) and each element in the path except for the element that’s farthest from the root in the directory hierarchy. Path getRoot(): Return the path’s root component as a Path object, or return null when this path doesn’t have a root component. boolean isAbsolute(): Return true when the path is absolute (complete in that it doesn’t need to be combined with other path information to locate a file); otherwise, return false. Path subpath(int beginIndex, int endIndex): Return a relative Path that’s a subsequence of the name elements of this path. The name elements range from beginIndex to one less than endIndex. IllegalArgumentException is thrown when beginIndex is negative or greater than or equal to the number of elements; or when endIndex is less than or equal to beginIndex or larger than the number of elements. String toString(): Return the string representation of this path. The separator character that’s used to separate name elements is obtained by invoking the current FileSystem object’s getSeparator() method. Listing 3 presents the source code to a small application that demonstrates these path information-retrieval methods. It also shows the proper way to add a root directory to a path. Listing 3. RetrievePathInfo.java import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; public class RetrievePathInfo { public static void main(String[] args) { dumpPathInfo(Paths.get("a", "b", "c")); FileSystem fs = FileSystems.getDefault(); Iterable<Path> roots = fs.getRootDirectories(); for(Path root: roots) { dumpPathInfo(Paths.get(root.toString(), "a", "b", "c")); break; } } static void dumpPathInfo(Path path) { System.out.printf("Path: %s%n", path); System.out.printf("Filename: %s%n", path.getFileName()); System.out.println("Components"); for (int i = 0; i < path.getNameCount(); i++) System.out.printf(" %s%n", path.getName(i)); System.out.printf("Parent: %s%n", path.getParent()); System.out.printf("Root: %s%n", path.getRoot()); System.out.printf("Absolute: %b%n", path.isAbsolute()); System.out.printf("Subpath [0, 2): %s%n%n", path.subpath(0, 2)); } } Listing 3: RetrievePathInfo.java Listing 3 creates two paths — a relative path and an absolute path — and invokes the previously listed methods to return various kinds of information about them. For the second path, FileSystem‘s Iterable<Path> getRootDirectories() method is called to retrieve the default file system’s root directories. The first root directory is prepended to the path prior to dumping path information. Compile Listing 3 (javac RetrievePathInfo.java) and run the application (java RetrievePathInfo). On a Windows platform, I observe the following output: Path: abc Filename: c Components a b c Parent: ab Root: null Absolute: false Subpath [0, 2): ab Path: C:abc Filename: c Components a b c Parent: C:ab Root: C: Absolute: true Subpath [0, 2): ab Notice that isAbsolute() returns true for the path with the prepended root directory. If you prepended only the file system’s separator, as in Paths.get(FileSystems.getDefault().getSeparator() + "a", "b", "c"), isAbsolute() would return false for this path on a Windows platform — an absolute path on a Windows platform must also include a drive specifier; for example, C:. More path operations Q: What other kinds of path operations can I perform on a Path object? A: The Path interface provides methods for removing path redundancies, creating a relative path between two paths, joining two paths, and more. The first three operations are performed by the following methods: Path normalize() Path relativize(Path other) Path resolve(Path other) (and the Path resolve(String other) variant) normalize() is useful for removing redundancies from a path. For example, reports/./2015/jan includes the redundant . (current directory) element. When normalized, this path becomes the shorter reports/2015/jan. relativize() creates a relative path between two paths. For example, given current directory jan in the reports/2015/jan hierarchy, the relative path to navigate to reports/2016/mar is ../../2016/mar. resolve() is the inverse of relativize(). It lets you join a partial path (a path without a root element) to another path. For example, resolving apr against reports/2015 results in reports/2015/apr. Listing 4 demonstrates these methods in the context of these and other examples. (For convenience, I hard-coded / in two places, but should have obtained a root directory instead.) Listing 4. MorePathOp.java import java.nio.file.Path; import java.nio.file.Paths; public class MorePathOp { public static void main(String[] args) { Path path1 = Paths.get("reports", ".", "2015", "jan"); System.out.println(path1); System.out.println(path1.normalize()); path1 = Paths.get("reports", "2015", "..", "jan"); System.out.println(path1.normalize()); System.out.println(); path1 = Paths.get("reports", "2015", "jan"); System.out.println(path1); System.out.println(path1.relativize(Paths.get("reports", "2016", "mar"))); try { System.out.println(path1.relativize(Paths.get("/reports", "2016", "mar"))); } catch (IllegalArgumentException iae) { iae.printStackTrace(); } System.out.println(); path1 = Paths.get("reports", "2015"); System.out.println(path1); System.out.println(path1.resolve("apr")); System.out.println(path1.resolve("/apr")); } } Listing 4’s main() method first demonstrates the normalize() example involving the current directory. It then demonstrates this example involving the parent directory (..). The result is reports/jan. Next, main() demonstrates the previous relativize() example and then demonstrates that you cannot relativize a relative path against an absolute path. If you attempt to do so, IllegalArgumentException is thrown. Finally, main() demonstrates the previous resolve() example and then shows what happens when you resolve against an absolute path. Instead of reports/2015/apr, you obtain /apr. You can perform additional operations on a Path object, such as converting a Path to an absolute Path via Path toAbsolutePath(), and determining if two Paths are the same via the boolean equals(Object other) method. Testing file accessibility Q: How do I determine if a file is executable, readable, or writable? A: The Files class provides the following static methods for determining if a file is executable, readable, or writable: boolean isExecutable(Path path): Check that the file represented by path exists and that the virtual machine has appropriate privileges to execute it. Return true when the file exists and is executable; return false when the file doesn’t exist, execute access would be denied because the virtual machine has insufficient privileges, or access cannot be determined. boolean isReadable(Path path): Check that the file represented by path exists and that the virtual machine has appropriate privileges to open it for reading. Return true when the file exists and is readable; return false when the file doesn’t exist, read access would be denied because the virtual machine has insufficient privileges, or access cannot be determined. boolean isWritable(Path path): Check that the file represented by path exists and that the virtual machine has appropriate privileges to open it for writing. Return true when the file exists and is writable; return false when the file doesn’t exist, write access would be denied because the virtual machine has insufficient privileges, or access cannot be determined. You would typically call one of these methods before performing the appropriate execute, read, or write file operation. For example, you might check a file to learn if it’s writable before attempting to write to the file, as follows: if (!Files.isWritable(Paths.get("file"))) { System.out.println("file is not writable"); return; } byte[] data = { /* some comma-separated list of byte data items */ }; Files.write(Paths.get("file"), data); However, there’s a problem with this code. After checking to see if file is writable, and before writing to the file, an external process might access the file and make it read-only. The resulting file-writing code would then fail. The race condition between time of check and time of use is commonly known as Time of check to time of use (TOCTTOU — pronounced TOCK-too), and is why the Javadoc says that the results of these methods are immediately outdated. TOCTTOU is probably not much of a problem for many applications where there is extremely low probability of external interference. However, it’s a significant problem for security-sensitive applications where a hacker might use it to steal user authentication information. For these applications, you might want to avoid isWritable() and its companions, and place your file I/O code in a try/catch construct instead. Testing file or directory existence Q: How do I determine if a file or directory exists or doesn’t exist? A: The Files class provides the following static methods for determining if a file or directory exists or doesn’t exist: boolean exists(Path path, LinkOption... options): Test the file/directory represented by path to determine if it exists. By default, symbolic links are followed. However, if you pass LinkOption.NOFOLLOW_LINKS to options, symbolic links are not followed. Return true when the file/directory exists; return false when the file/directory doesn’t exist or its existence cannot be determined. boolean notExists(Path path, LinkOption... options): Test the file/directory represented by path to determine if it doesn’t exist. By default, symbolic links are followed. However, if you pass LinkOption.NOFOLLOW_LINKS to options, symbolic links are not followed. Return true when the file/directory doesn’t exist; return false when the file/directory exists or its existence cannot be determined. You would probably call exists() before attempting an operation that throws an exception when the file doesn’t exist. For example, you might test for existence before deleting a file: Path path = Paths.get("file"); if (Files.exists(path)) Files.delete(path); Note that !exists(path) isn’t equivalent to notExists(path) (probably because !exists() isn’t atomic, whereas notExists() is atomic). Also, if exists() and notExists() return false, the existence of the file cannot be verified. Finally, as with the accessibility methods, the results of these methods are immediately outdated and should be avoided or at least used very carefully in security-sensitive applications. More testing operations Q: What other kinds of testing operations can I perform on a Path object? A: The Files class provides static methods for determining if a path is a directory, if a path is hidden, if a path is a regular file, if two paths identify the same file, and if a path is a symbolic link: boolean isDirectory(Path path, LinkOption... options) — specify LinkOption.NOFOLLOW_LINKS when you don’t want this method to follow symbolic links. boolean isHidden(Path path) boolean isRegularFile(Path path, LinkOption... options) — specify LinkOption.NOFOLLOW_LINKS when you don’t want this method to follow symbolic links. boolean isSameFile(Path path, Path path2) boolean isSymbolicLink(Path path) Consider isHidden(Path), which returns true when the file identified by the Path argument is considered to be hidden. Listing 5’s application uses this method to filter the names of a directory’s hidden files, which are then output. Listing 5. ListHiddenFiles.java import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class ListHiddenFiles { public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("usage: java ListHiddenFiles directory"); return; } Path dir = Paths.get(args[0]); DirectoryStream<Path> stream = Files.newDirectoryStream(dir); for (Path file: stream) if (Files.isHidden(file)) System.out.println(file); } } main() first validates the command line, whose solitary argument identifies the directory to search for hidden files. This String object is then converted into a Path object. The Path object is passed to Files‘s DirectoryStream<Path> newDirectoryStream(Path dir) method, which returns a java.nio.file.DirectoryStream object for iterating over all entries in the directory identified by the path. DirectoryStream implements Iterable, enabling this object be used with the enhanced for statement to loop over directory entries. For each entry, isHidden() is called; only those entries where this method returns true are output. Compile Listing 5 (javac ListHiddenFiles.java) and run the application; for example, java ListHiddenFiles C:. On my Windows platform, I observed the following output: C:hiberfil.sys C:pagefile.sys Getting and setting single attributes Q: How do I get or set a Path object’s attributes? A: Files provides several static methods for getting or setting a Path object’s attributes: Object getAttribute(Path path, String attribute, LinkOption... options) FileTime getLastModifiedTime(Path path, LinkOption... options) UserPrincipal getOwner(Path path, LinkOption... options) Set<PosixFilePermission> getPosixFilePermissions(Path path, LinkOption... options) boolean isDirectory(Path path, LinkOption... options) boolean isHidden(Path path) boolean isRegularFile(Path path, LinkOption... options) boolean isSymbolicLink(Path path) Path setAttribute(Path path, String attribute, Object value, LinkOption... options) Path setLastModifiedTime(Path path, FileTime time) Path setOwner(Path path, UserPrincipal owner) Path setPosixFilePermissions(Path path, Set<PosixFilePermission> perms) long size(Path path) Previously, I presented isDirectory(), isHidden(), isRegularFile(), and isSymbolicLink(), which classify a Path object. These methods also return a Path object’s directory, hidden, regular file, and symbolic link attribute values. Listing 6 presents a small application that obtains a Path object’s last-modified time, owner, and size via the getLastModifiedTime(Path), getOwner(Path), and size(Path) methods; and outputs these values. Listing 6. PathAttrib.java (version 1) import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class PathAttrib { public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("usage: java PathAttrib path-object"); return; } Path path = Paths.get(args[0]); System.out.printf("Last modified time: %s%n", Files.getLastModifiedTime(path)); System.out.printf("Owner: %s%n", Files.getOwner(path)); System.out.printf("Size: %d%n", Files.size(path)); } } Compile Listing 6 (javac PathAttrib.java) and run the application. For example, I observe the following output from java PathAttrib PathAttrib.java: Last modified time: 2015-03-18T17:56:15.802635Z Owner: Owner-PCOwner (User) Size: 604 Q: What is an attribute view and how do I work with it? A: The Javadoc for various attribute methods (e.g., getAttribute(), getPosixFilePermissions(), setAttribute(), and setPosixFilePermissions()) refers to interface types in the java.nio.file.attribute package. These interfaces identify different attribute views, which are groups of related file attributes. An attribute view maps to a file system implementation (e.g., POSIX) or to a common functionality (e.g., file ownership). NIO.2 provides AttributeView as the root of its attribute view interface hierarchy. This interface declares a getName() method for returning an attribute view’s name. AttributeView is extended by FileAttributeView, which “is a read-only or updatable view of non-opaque values associated with a file in a file system.” FileAttributeView offers no methods, but serves as a superinterface for more specific file-oriented attribute views: BasicFileAttributeView: a view of basic attributes that must be supported by all file system implementations DosFileAttributeView: an extension of the basic attribute view with the legacy DOS operating system read-only, hidden, system file, and archived flags PosixFileAttributeView: an extension of the basic attribute view with attributes supported on file systems that support the POSIX family of standards (e.g., UNIX). These attributes include file owner, group owner, and the nine related access permissions. FileOwnerAttributeView: supported by any file system implementation that supports the concept of a file owner AclFileAttributeView: supports reading or updating a file’s Access Control Lists (ACL) UserDefinedFileAttributeView: enables support of user-defined metadata Note that a file system implementation might support only the basic file attribute view, or it might support several of the aforementioned file attribute views (and even support other attribute views not included in the previous list). Files provides the <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) generic method for returning a specific type of file attribute view; for example, AclFileAttributeView view = Files.getFileAttributeView(path, AclFileAttributeView.class);. This method returns null when the attribute view isn’t available. You typically don’t deal with FileAttributeView or its subinterfaces. Instead, you would work with the getAttribute() and setAttribute() methods. Each method’s attribute parameter requires a string adhering to the following syntax: [view-name:]attribute-name view-name identifies a FileAttributeView that identifies a set of file attributes. If specified, it is one of basic, dos, posix, owner, or acl. When not specified, the view name defaults to basic, which is the attribute view that identifies the basic set of file attributes common to many file systems. (There is no name for user-defined file attribute views.) attribute-name is the attribute’s name. The various attribute view interfaces identify specific names. Each method throws java.lang.UnsupportedOperationException when the attribute view isn’t supported. To find out what attribute views are supported, invoke FileSystem‘s Set<String> supportedFileAttributeViews() method. Listing 7 presents an application that outputs the default file system’s supported attribute views, and outputs the values of all attributes in the (always supported) basic and (when supported) acl and dos attribute views for the specified path. Listing 7. PathAttrib.java (version 2) import java.io.IOException; import java.nio.file.Files; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; public class PathAttrib { public static void main(String[] args) throws IOException { boolean isACL = false; boolean isDOS = false; FileSystem defFS = FileSystems.getDefault(); for (String fileAttrView : defFS.supportedFileAttributeViews()) { System.out.printf("Default file system supports: %s%n", fileAttrView); if (fileAttrView.equals("acl")) isACL = true; if (fileAttrView.equals("dos")) isDOS = true; } System.out.println(); if (args.length != 1) { System.err.println("usage: java PathAttrib path-object"); return; } Path path = Paths.get(args[0]); // Output basic attributes, which are always supported. System.out.println("Basic attributes:"); String[] attrNames = { "lastModifiedTime", "lastAccessTime", "creationTime", "size", "isRegularFile", "isDirectory", "isSymbolicLink", "isOther", // something other that a regular file, directory, or // symbolic link "fileKey" // an object that uniquely identifies the given file, or // null when a file key is not available. }; for (String attrName: attrNames) System.out.printf("%s: %s%n", attrName, Files.getAttribute(path, "basic:" + attrName)); System.out.println(); // Output ACL owner attribute when this view is supported. if (isACL) { System.out.println("ACL attributes:"); System.out.printf("Owner: %s%n%n", Files.getAttribute(path, "acl:owner")); } // Output DOS attributes when this view is supported. if (isDOS) { System.out.println("DOS attributes:"); attrNames = new String[] { "readonly", "hidden", "system", "archive" }; for (String attrName: attrNames) System.out.printf("%s: %s%n", attrName, Files.getAttribute(path, "dos:" + attrName)); System.out.println(); } } } Compile Listing 7 and run the application. For example, I observe the following output from java PathAttrib PathAttrib.java: Default file system supports: acl Default file system supports: basic Default file system supports: owner Default file system supports: user Default file system supports: dos Basic attributes: lastModifiedTime: 2015-03-18T19:36:47.435624Z lastAccessTime: 2015-03-18T19:21:16.681388Z creationTime: 2015-03-18T19:21:16.681388Z size: 2417 isRegularFile: true isDirectory: false isSymbolicLink: false isOther: false fileKey: null ACL attributes: Owner: Owner-PCOwner (User) DOS attributes: readonly: false hidden: false system: false archive: true Getting and setting multiple attributes Q: I’ve heard that it’s more efficient to read attributes in bulk as opposed to reading them individually. How do I read attributes in bulk? A: Files declares a pair of static methods for reading attributes in bulk: <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) The first method returns a java.nio.file.attribute.BasicFileAttributes or subinterface object containing all attribute values for the specified type. Call various methods on the returned object to obtain these values. The second method returns a map containing the names and values for all attributes identified by the attributes argument, which has the form [view-name:]attribute-list; for example, "posix:permissions,owner,size". Listing 8 presents an application that demonstrates these methods. The application calls each method to obtain the basic file attributes for a Path object argument and then outputs the values. Listing 8. PathAttrib.java (version 3) import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.util.Map; import java.util.TreeMap; public class PathAttrib { public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("usage: java PathAttrib path-object"); return; } Path path = Paths.get(args[0]); BasicFileAttributes attrs; attrs = Files.readAttributes(path, BasicFileAttributes.class); System.out.printf("Basic Attributes for %s...%n%n", path); System.out.printf("creationTime: %s%n", attrs.creationTime()); System.out.printf("fileKey: %s%n", attrs.fileKey()); System.out.printf("isDirectory: %b%n", attrs.isDirectory()); System.out.printf("isOther: %b%n", attrs.isOther()); System.out.printf("isRegularFile: %b%n", attrs.isRegularFile()); System.out.printf("isSymbolicLink: %b%n", attrs.isSymbolicLink()); System.out.printf("lastAccessTime: %s%n", attrs.lastAccessTime()); System.out.printf("lastModifiedTime: %s%n", attrs.lastModifiedTime()); System.out.printf("size: %d%n", attrs.size()); System.out.println(); Map<String, Object> attrMap = new TreeMap<>(Files.readAttributes(path, "*")); for (Map.Entry<String, Object> entry: attrMap.entrySet()) System.out.printf("%s: %s%n", entry.getKey(), entry.getValue()); } } In Listing 8, I pass "*" as the second argument to the second readAttributes() method call to indicate that all attributes for the basic attribute view are to be returned — not specifying a view name results in basic as the default name. Also, I pass the second readAttributes() method call’s returned map to the java.util.TreeMap constructor to obtain a sorted map, so that entries will be output in sorted order. Compile Listing 8 and run the application. For example, I observe the following output from java PathAttrib PathAttrib.java: Basic Attributes for PathAttrib.java... creationTime: 2015-03-18T20:20:43.635406Z fileKey: null isDirectory: false isOther: false isRegularFile: true isSymbolicLink: false lastAccessTime: 2015-03-18T20:20:43.635406Z lastModifiedTime: 2015-03-18T20:35:20.446953Z size: 1618 creationTime: 2015-03-18T20:20:43.635406Z fileKey: null isDirectory: false isOther: false isRegularFile: true isSymbolicLink: false lastAccessTime: 2015-03-18T20:20:43.635406Z lastModifiedTime: 2015-03-18T20:35:20.446953Z size: 1618 More attribute operations Q: What other kinds of attribute operations can I perform on a Path object? A: You can read the attribute values for the file store (a storage pool, device, partition, volume, concrete file system or other implementation-specific means of file storage) that’s associated with a Path object. Accomplish this task as follows: Invoke Files‘s FileStore getFileStore(Path path) method to obtain the file store. Invoke java.nio.file.FileStore‘s Object getAttribute(String attribute) method to return the value for the given file store attribute. You must adhere to the view-name:attribute-name syntax for attribute‘s string. Listing 9 presents the source code to a small application that shows you how to use getAttribute() to obtain the values for the NTFS file store implementation’s attributes, which are then output. Listing 9. PathAttrib.java (version 4) import java.io.IOException; import java.nio.file.Files; import java.nio.file.FileStore; import java.nio.file.Path; import java.nio.file.Paths; public class PathAttrib { public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("usage: java PathAttrib path-object"); return; } Path path = Paths.get(args[0]); FileStore fs = Files.getFileStore(path); if (fs.type().equals("NTFS")) { String[] attrNames = { "totalSpace", "usableSpace", "unallocatedSpace", "volume:vsn", "volume:isRemovable", "volume:isCdrom" }; for (String attrName: attrNames) System.out.println(fs.getAttribute(attrName)); } } } When FileStore‘s String type() method returns NTFS as the file store type, the underlying file store implementation recognizes the attributes identified in the listing. (Instead of obtaining the total space, usable space, and unallocated space via getAttribute() calls, you should use the more convenient long getTotalSpace(), long getUsableSpace(), and long getUnallocatedSpace() methods.) Compile Listing 9 and run the application. For example, I observe the following output from java PathAttrib PathAttrib.java — nothing would output when NTFS isn’t the file store type: 499808989184 229907410944 229907410944 1079402743 false false FileStore also declares a <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) method for obtaining a FileStoreAttributeView (an attribute view that’s a read-only or updatable view of a file store’s attributes) of the given type. Because there are no official subinterfaces for FileStoreAttributeView, this method is probably only useful for custom file stores and API packages. What’s next? In Part 3, I wrap up this series by presenting more advanced recipes for copying directory hierarchies, finding files, and watching directories for changes. You’ll learn about file visitors, globs, and watch services. download Download the source Get the source code for this post’s applications. Created by Jeff Friesen for JavaWorld The following software was used to develop the post’s code: 64-bit JDK 7u6 The post’s code was tested on the following platform(s): JVM on 64-bit Windows 7 SP1 Java