Conceptualizing the File System
file within the storage device holds data. Files are organized into hierarchies using directories.directory is a location that can contain files as well as other directories.file system is in charge of reading and writing data within a computer. Different operating systems use different file systems to manage their data.root directory is the topmost directory in the file system, ex: Windows C:\ and Linux /path is a representation of a file or directory within a file system.File Separators, Unix-based systems use the forward slash, /, for paths, whereas Windows-based systems use the backslash, \absolute path of a file or directory is the full path from the root directory to the file or directory,relative path of a file or directory is the path from the current working directory to the file or directory.path symbol is one of a reserved series of characters with special meaning in some file systems.symbolic link is a special file within a file system that serves as a reference or pointer to another file or directory.NOTE
> [!NOTE]I/O APIs do not support symbolic links, NIO.2 includes full support for creating, detecting, and navigating symbolic links within the file system.
both the I/O and NIO.2 classes can interact with a URI.
A uniform resource identifier (URI) is a string of characters that identifies a resource.
It begins with a schema that indicates the resource type, followed by a path value such as file:// for local file systems and http://, https://, and ftp:// for remote file systems.
Operating System File Separators
Java offers a system property to retrieve the local separator character for the current environment:
System.out.print(System.getProperty("file.separator"));
we used two backslashes (\\) in the path String, such as C:\data\zoo.txt.
When the compiler sees a \\ inside a String expression, it interprets it as a single \ value.
Creating a File or Path
java.io.File class is cread by calling its constructor.java.nio.file.Path interface, obtain a Path object is to use a static factory method defined on Path or Paths.java.nio.file.Pathjava.nio.file.Pathsjava.io.nio.file.Files NIO.2 helper class, Files operates on Path instancesCreating a File
File zooFile1 = new File("/home/tiger/data/stripes.txt");
File zooFile2 = new File("/home/tiger", "data/stripes.txt");
File parent = new File("/home/tiger");
File zooFile3 = new File(parent, "data/stripes.txt");
System.out.println(zooFile1.exists());The File class is created by calling its constructor.
This code shows three different constructors:
File zooFile1 = new File("/home/tiger/data/stripes.txt");
File zooFile2 = new File("/home/tiger", "data/stripes.txt");
File parent = new File("/home/tiger");
File zooFile3 = new File(parent, "data/stripes.txt");
System.out.println(zooFile1.exists());All three create a File object that points to the same location on disk.
If we passed null as the parent to the final constructor, it would be ignored, and the method would behave the same way as the single String constructor.
For fun, we also show how to tell if the file exists on the file system.
Creating a Path
//Path static method of()
Path zooPath1 = Path.of("/home/tiger/data/stripes.txt");
Path zooPath2 = Path.of("/home", "tiger", "data", "stripes.txt");
//Paths factory class static method get()
Path zooPath3 = Paths.get("/home/tiger/data/stripes.txt");
Path zooPath4 = Paths.get("/home", "tiger", "data", "stripes.txt");
System.out.println(Files.exists(zooPath1));All four of these examples point to the same reference on disk:
//Path static method of()
Path zooPath1 = Path.of("/home/tiger/data/stripes.txt");
Path zooPath2 = Path.of("/home", "tiger", "data", "stripes.txt");
//Paths factory class static method get()
Path zooPath3 = Paths.get("/home/tiger/data/stripes.txt");
Path zooPath4 = Paths.get("/home", "tiger", "data", "stripes.txt");
System.out.println(Files.exists(zooPath1));Both methods allow passing a varargs parameter to pass additional path elements. The values are combined and automatically separated by the operating system–dependent file separator. We also show the Files helper class, which can check if the file exists on the file system.
Switching between File and Path
File file = new File("rabbit");
Path nowPath = file.toPath();
File backToFile = nowPath.toFile();When working with newer applications, you should rely on NIO.2’s Path interface, as it contains a lot more features.
File file = new File("rabbit");
Path nowPath = file.toPath();
File backToFile = nowPath.toFile();Obtaining a Path from the FileSystems Class
FileSystems class creates instances of the abstract FileSystem class.Path zooPath1 = FileSystems.getDefault().getPath("/home/tiger/data/stripes.txt");
Path zooPath2 = FileSystems.getDefault().getPath("/home", "tiger", "data", "stripes.txt");Reviewing I/O and NIO.2 Relationships
FIGURE 14.3 I/O and NIO.2 class and interface relationships
TABLE 14.2 Options for creating File and Path
java.io.File
java.nio.file.Path
java.nio.file.Paths
java.nio.file.FileSystem
java.nio.file.FileSystems
TABLE 14.3 Common File and Path operations
TABLE 14.3 Common File and Path operations
getName()getFileName()getParent()getParent()isAbsolute()isAbsolute()TABLE 14.4 Common File and Files operations
TABLE 14.4 Common File and Files operations
* Deletes file/directory
* delete()
* deleteIfExists(Path p) throws IOException
* Checks if file/directory exists
* exists()
* exists(Path p, LinkOption… o)
* Retrieves absolute path of file/directory
* getAbsolutePath()
* toAbsolutePath() <–this is Path interface method
* Checks if resource is directory
* isDirectory()
* isDirectory(Path p, LinkOption… o)
* Checks if resource is file
* isFile()
* isRegularFile(Path p, LinkOption… o)
* Returns the time the file was last modified
* lastModified()
* getLastModifiedTime(Path p, LinkOption… o) throws IOException
* Retrieves number of bytes in file
* length()
* size(Path p) throws IOException
* Lists contents of directory
* listFiles()
* list(Path p) throws IOException
* Creates directory
* mkdir()
* createDirectory(Path p, FileAttribute… a) throws IOException
* Creates directory including any nonexistent parent directories
* mkdirs()
* createDirectories(Path p, FileAttribute… a) throws IOException
* Renames file/directory denoted
* renameTo(File dest)
* move(Path src, Path dest, CopyOption… o) throws IOException
I/O API example:
10: public static void io() {
11: var file = new File("C:\\data\\zoo.txt");
12: if (file.exists()) {
13: System.out.println("Absolute Path: " + file.getAbsolutePath());
14: System.out.println("Is Directory: " + file.isDirectory());
15: System.out.println("Parent Path: " + file.getParent());
16: if (file.isFile()) {
17: System.out.println("Size: " + file.length());
18: System.out.println("Last Modified: " + file.lastModified());
19: } else {
20: for (File subfile : file.listFiles()) {
21: System.out.println(" " + subfile.getName());
22: } } } }If the path provided points to a valid file, the program outputs something similar to the following due to the if statement on line 16:
Absolute Path: C:\data\zoo.txt Is Directory: false Parent Path: C:\data Size: 12382 Last Modified: 1650610000000
if the path provided points to a valid directory, such as C:\data, the program outputs something similar to the following, thanks to the else block:
Absolute Path: C:\data Is Directory: true Parent Path: C:\ employees.txt zoo.txt zoo-backup.txt
NIO.2 example:
25: public static void nio() throws IOException {
26: var path = Path.of("C:\\data\\zoo.txt");
27: if (Files.exists(path)) {
28: System.out.println("Absolute Path: " + path.toAbsolutePath());
29: System.out.println("Is Directory: " + Files.isDirectory(path));
30: System.out.println("Parent Path: " + path.getParent());
31: if (Files.isRegularFile(path)) {
32: System.out.println("Size: " + Files.size(path));
33: System.out.println("Last Modified: "
34: + Files.getLastModifiedTime(path));
35: } else {
36: try (Stream stream = Files.list(path)) {
37: stream.forEach(p ->
38: System.out.println(" " + p.getName()));
39: } } } }Files.size(), Files.getLastModifiedTime(), and Files.list() throw an IOException.Closing the Stream
Stream object inside a try-with-resources?
36: try (Stream stream = Files.list(path)) {
37: stream.forEach(p ->
38: System.out.println(" " + p.getName()));
39: }Handling Methods That Declare IOException
Many of the methods presented in this chapter declare IOException.
Common causes of a method throwing this exception include the following:
Methods that access or change files and directories, such as those in the Files class, often declare IOException. There are exceptions to this rule, as we will see. For example, the method Files.exists() does not declare IOException. If it did throw an exception when the file did not exist, it would never be able to return false!
As a rule of thumb, if a NIO.2 method declares an IOException, it usually requires the paths it operates on to exist.
Providing NIO.2 Optional Parameters
TABLE 14.5 Common NIO.2 method arguments
LinkOption.NOFOLLOW_LINKS-Do not follow symbolic links.StandardCopyOption.ATOMIC_MOVE- Move file as atomic file system operation.StandardCopyOption.COPY_ATTRIBUTES-Copy existing attributes to new file.StandardCopyOption.REPLACE_EXISTING-Overwrite file if it already exists.StandardOpenOption.APPEND-If file is already open for write, append to the end.StandardOpenOption.CREATE-Create new file if it does not exist.StandardOpenOption.CREATE_NEW-Create new file only if it does not exist; fail otherwise.StandardOpenOption.READ-Open for read access.StandardOpenOption.TRUNCATE_EXISTING-If file is already open for write, erase file and append to beginning.StandardOpenOption.WRITE-Open for write access.FileVisitOption.FOLLOW_LINKS-Follow symbolic links.What the following call to Files.exists() with the LinkOption does in the following code snippet?
Path path = Paths.get("schedule.xml");
boolean exists = Files.exists(path, LinkOption.NOFOLLOW_LINKS);LinkOption.NOFOLLOW_LINKS means the default behavior will be overridden, and the method will check whether the symbolic link itself exists.For example, the Files.move() method takes a CopyOption vararg so it can take enums of different types, and more options can be added over time.
public static Path copy(Path source, Path target, CopyOption... options) throws IOException
LinkOption and StandardCopyOption Implemented CopyOption
void copy(Path source, Path target) throws IOException {
Files.move(source, target,
LinkOption.NOFOLLOW_LINKS,
StandardCopyOption.ATOMIC_MOVE);
}Files.move() method takes a CopyOption vararg so it can take enums of different types, and more options can be added over time.
public static Path copy(Path source, Path target, CopyOption... options) throws IOException
LinkOption and StandardCopyOption Implemented CopyOptionAnswer
Interacting with NIO.2 Paths
Path instances are immutable
In the following example, the Path operation on the second line is lost since p is immutable:
Path p = Path.of("whale");
p.resolve("krill");
System.out.println(p); // whaleMany of the methods available in the Path interface transform the path value in some way and return a new Path object, allowing the methods to be chained. We demonstrate chaining in the following example, the details of which we discuss in this section of the chapter:
Path.of("/zoo/../home").getParent().normalize().toAbsolutePath();Viewing the PathString toString()int getNameCount()Path getName(int index)
Path path = Paths.get("/land/hippo/harry.happy");
System.out.println("The Path Name is: " + path);
for(int i=0; i<path.getNameCount(); i++)
System.out.println(" Element " + i + " is: " + path.getName(i));The Path interface contains three methods to retrieve basic information about the path representation.
The toString() method returns a String representation of the entire path. In fact, it is the only method in the Path interface to return a String.
The getNameCount() and getName() methods are often used together to retrieve the number of elements in the path and a reference to each element, respectively. These two methods do not include the root directory as part of the path.
The code prints the following:
The Path Name is: /land/hippo/harry.happy Element 0 is: land Element 1 is: hippo Element 2 is: harry.happy
The getNameCount() and getName() methods do not consider the root part of the path.
var p = Path.of("/");
System.out.print(p.getNameCount()); // 0
System.out.print(p.getName(0)); // IllegalArgumentExceptionNotice that if you try to call getName() with an invalid index, it will throw an exception at runtime.
Creating Part of the Pathsubpath()
var p = Paths.get("/mammal/omnivore/raccoon.image");
System.out.println("Path is: " + p);
for (int i = 0; i < p.getNameCount(); i++) {
System.out.println(" Element " + i + " is: " + p.getName(i));
}
System.out.println();
System.out.println("subpath(0,3): " + p.subpath(0, 3));
System.out.println("subpath(1,2): " + p.subpath(1, 2));
System.out.println("subpath(1,3): " + p.subpath(1, 3));The Path interface includes the subpath() method to select portions of a path. It takes two parameters: an inclusive beginIndex and an exclusive endIndex.
The output of this code snippet is the following:
Path is: /mammal/omnivore/raccoon.image Element 0 is: mammal Element 1 is: omnivore Element 2 is: raccoon.image subpath(0,3): mammal/omnivore/raccoon.image subpath(1,2): omnivore subpath(1,3): omnivore/raccoon.image
Like getNameCount() and getName(), subpath() is zero-indexed and does not include the root. Also like getName(), subpath() throws an exception if invalid indices are provided.
var q = p.subpath(0, 4); // IllegalArgumentException var x = p.subpath(1, 1); // IllegalArgumentException
The first example throws an exception at runtime, since the maximum index value allowed is 3. The second example throws an exception since the start and end indexes are the same, leading to an empty path value.
Accessing Path Elements
getFileName()getParent()getRoot()getFileName() method returns the Path element of the current file or directory,getParent() returns the full path of the containing directory.