Java Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In Java, Path and File are classes responsible for file I/O operations. They perform the same functions but belong to different packages.

In this tutorial, we'll discuss the differences between these two classes. We'll start with a quick class recap. Then, we'll talk about some legacy drawbacks. Finally, we'll learn how to migrate functionalities between both APIs.

2. java.io.File Class

Since the very first versions, Java has delivered its own java.io package, which contains nearly every class we might ever need to perform input and output operations. The File class is an abstract representation of file and directory pathnames:

File file = new File("baeldung/tutorial.txt");

Instances of the File class are immutable – once created, the abstract pathname represented by this object will never change.

3. java.nio.file.Path Class

The Path class forms part of the NIO2 update, which came to Java with version 7. It delivers an entirely new API to work with I/O. Moreover, like the legacy File class, Path also creates an object that may be used to locate a file in a file system.

Likewise, it can perform all the operations that can be done with the File class:

Path path = Paths.get("baeldung/tutorial.txt");

Instead of using a constructor as we do with the File API, we create a Path instance using the static java.nio.file.Paths.get() method.

4. File Class Drawbacks

After this short recap of the two classes, let's now discuss both APIs and answer the question: If they deliver the same functionality, why did Oracle decide to bring a new API, and which one should I use?

As we know, the java.io package was delivered with the first release of the Java JDK, allowing us to perform I/O actions. Since then, many developers have reported many drawbacks, missing functionality, and problems with some of its capabilities.

4.1. Error Handling

The most common problem is poor error handling. Many methods don't tell us any details about the encountered problem or even throw exceptions at all.

Let's assume we have a simple program that deletes a file:

File file = new File("baeldung/tutorial.txt");
boolean result = file.delete();

This code compiles and runs successfully without any error. Of course, we have a result flag containing a false value, but we don't know the reason for this failure. The file might not exist, or the program may not have permission to delete it.

We can now rewrite the same functionality using the newer NIO2 API:

Path path = Paths.get("baeldung/tutorial.txt");
Files.delete(path);

Now, the compiler requires us to handle an IOException. Moreover, a thrown exception has details about its failure that will tell you, for example, if the file does not exist.

4.2. Metadata Support

The File class in the java.io package has poor metadata support, which leads to problems across different platforms with I/O operations requiring meta-information about files.

The metadata may also include permissions, file owner, and security attributes. Due to this, the File class doesn't support symbolic links at all, and the rename() method doesn't work consistently across different platforms.

4.3. Method Scaling and Performance

There is also a performance problem because the methods of the File class don't scale. It leads to problems with some directories with a large number of files. Listing the contents of a directory could result in a hang, causing memory resource problems. Finally, it could lead to having a Denial of Service.

Due to some of these drawbacks, Oracle developed the improved NIO2 API. Developers should start new projects using this new java.nio package instead of legacy classes, where possible.

5. Mapping Functionality

In order to fix some gaps in the java.io package, Oracle prepared its own drawbacks summary, helping developers to migrate between APIs.

The NIO2 package delivers all the legacy functionalities, including improvements for the drawbacks mentioned. Due to a large number of applications that might still use this legacy API, Oracle currently doesn't plan to deprecate or remove the old API in future releases.

In the new API, instead of instance methods, we use static ones from the java.nio.file.Files class. Let's now quickly compare those APIs.

5.1. File and Path Instances

The major difference is, of course, the package and class name:

java.io.File file = new java.io.File("baeldung/tutorial.txt");
java.nio.file.Path path = java.nio.file.Paths.get("baeldung/tutorial.txt");

Here, we build a File object via the constructor, while we obtain a Path by using a static method. We can also resolve complex paths using multiple arguments:

File file = new File("baeldung", "tutorial.txt");
Path path = Paths.get("baeldung", "tutorial.txt");

And, we can achieve the same result by chaining the resolve() method:

Path path2 = Paths.get("baeldung").resolve("tutorial.txt");

Moreover, we can convert objects between APIs using toPath() and toFile() methods:

Path pathFromFile = file.toPath();
File fileFromPath = path.toFile();

5.2. Managing Files and Directories

Both APIs deliver methods to manage files and directories. We'll demonstrate this using the previously created instance objects.

To create files, we can use the createNewFile() and Files.createFile() methods:

boolean result = file.createNewFile();
Path newPath = Files.createFile(path);

To create a directory, we need to use mkdir() or Files.createDirectory():

boolean result = file.mkdir();
File newPath = Files.createDirectory(path);

There are additional variants of those methods to include all non-existing subdirectories, via the mkdirs() and Files.createDirectories() methods:

boolean result = file.mkdirs();
File newPath = Files.createDirectories(path);

When we want to rename or move a file, we need to create another instance object and use renameTo() or Files.move():

boolean result = file.renameTo(new File("baeldung/tutorial2.txt"));
Path newPath = Files.move(path, Paths.get("baeldung/tutorial2.txt"));

To perform a delete operation, we use delete() or Files.delete():

boolean result = file.delete();
Files.delete(Paths.get(path));

Notice that legacy methods return a flag with a result set to false in case of any errors. NIO2 methods return a new Path instance, except for the delete operation, which throws an IOException when errors occur.

5.3. Reading Metadata

We can also obtain some basic information about files, such as permissions or types. As before, we need an instance object:

// java.io API
boolean fileExists = file.exists();
boolean fileIsFile = file.isFile();
boolean fileIsDir = file.isDirectory();
boolean fileReadable = file.canRead();
boolean fileWritable = file.canWrite();
boolean fileExecutable = file.canExecute();
boolean fileHidden = file.isHidden();

// java.nio API
boolean pathExists = Files.exists(path);
boolean pathIsFile = Files.isRegularFile(path);
boolean pathIsDir = Files.isDirectory(path);
boolean pathReadable = Files.isReadable(path);
boolean pathWritable = Files.isWritable(path);
boolean pathExecutable = Files.isExecutable(path);
boolean pathHidden = Files.isHidden(path);

5.4. Pathname Methods

In the end, let's quickly look at methods in the File class for getting the filesystem path. Be aware that, unlike the previous examples, most of them are performed directly on object instances.

To get absolute or canonical paths, we can use:

// java.io API
String absolutePathStr = file.getAbsolutePath();
String canonicalPathStr = file.getCanonicalPath();

// java.nio API
Path absolutePath = path.toAbsolutePath();
Path canonicalPath = path.toRealPath().normalize();

While the Path object is immutable, it returns a new instance. Moreover, the NIO2 API has the toRealPath() and normalize() methods that we can use to remove redundancies.

Conversion to URI can be done by using the toUri() methods:

URI fileUri = file.toURI();
URI pathUri = path.toUri();

Also, we can list the directory content:

// java.io API
String[] list = file.list();
File[] files = file.listFiles();

// java.nio API
DirectoryStream<Path> paths = Files.newDirectoryStream(path);

NIO2 API returns its own DirectoryStream object, which implements the Iterable interface.

6. Conclusion

Since Java 7, developers can now choose between two APIs to work with files. In this article, we discussed some of the different drawbacks and problems related to the java.io.File class.

To fix them, Oracle decided to deliver the NIO package, which brings the same functionality with massive improvements.

Then, we reviewed both APIs. Through examples, we learned how to migrate between them. We also learned that the java.io.File is now considered as legacy, and not recommended for new projects. However, there's no plan to deprecate and remove it.

As always, all code snippets from this article are available over on GitHub.

Java bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!