Legacy File I/O Code
Prior to the Java SE 7 release, the java.io.File class was the mechanism used for file I/O, but it had several drawbacks.
- Many methods didn't throw exceptions when they failed, so it was impossible to obtain a useful error message. For example, if a file deletion failed, the program would receive a "delete fail" but wouldn't know if it was because the file didn't exist, the user didn't have permissions, or there was some other problem.
- The rename method didn't work consistently across platforms.
- There was no real support for symbolic links.
- More support for metadata was desired, such as file permissions, file owner, and other security attributes.
- Accessing file metadata was inefficient.
- Many of the File methods didn't scale. Requesting a large directory listing over a server could result in a hang. Large directories could also cause memory resource problems, resulting in a denial of service.
- It was not possible to write reliable code that could recursively walk a file tree and respond appropriately if there were circular symbolic links.
Perhaps you have legacy code that uses java.io.File and would like to take advantage of the java.nio.file.Path functionality with minimal impact to your code.
The java.io.File class provides the toPath method, which converts an old style File instance to a java.nio.file.Path instance, as follows:
You can then take advantage of the rich feature set available to the Path class.
Java File vs Path
I’ve been using java.io.File and java.io.File*Stream since Java 1.1, a long time ago. Java 7 introduced a new file API named NIO2 containing, among others, the java.nio.file.Path and java.nio.file.Files classes. It took me a while to lose my habits and embrace the new API.
Spoiler: The most funny part of this article is at the end!
Quick comparison
file = new File(«path/to/file.txt»)
file = new File(parentFile, «file.txt»)
Files.list(path) .filter(filter) .collect(toList())
Some additional notes:
Path throws IOException more often than File , and rarely return a boolean to tell if something was done ( mkdirs() , delete() )
File is more object oriented than Path : I regret that size() , exists() … methods are not on the Path interface. This is probably due to the fact that this API was added in Java 7, but default methods on interfaces were added later in Java 8.
Path based InputStream /`OutputStream`s are less expensive from a GC point view. Thanks @kittster for mentionning this article from Cloudbees.
One liners
java.nio.file.Files allows to read, write, copy files in a single line:
| 1 | Write a binary file |
| 2 | Read a text file |
This nearly makes Guava IO and Commons IO useless. I regret that there isn’t any method out of the box to read/write a whole file as a single string.
Many APIs (JAXB, Jackson to name a few) don’t use Path`s to read/write files, the workaround is usually use an `InputStream or an OutputStream .
Multiple file systems
When the File is only for local files, Path can also be used to access remote files. A Path is associated to a FileSystem .
To create a new Path instances, there is not constructor ( Path is interface), we need to call a factory method. The above 2 lines are the same:
As the default file system is the local one, you get a path to a local file. Depending on the underlying file system, you’ll get a different implementation: sun.nio.fs.UnixPath , sun.nio.fs.WindowsPath …
With this trick in mind, we can read the content of a Zip file, as if we had extracted it:
| 1 | «Mount» the Zip file as a file system |
| 2 | zipPath is of type com.sun.nio.zipfs.ZipPath |
You can even plug additional file systems: ZIP, SFTP, SMB, WebDAV, SSH/SCP, Amazon S3, In memory, HDFS, … This almost means you can read a remote file as if it were local.
Java: Path vs File
For new applications written in Java 7, is there any reason to use a java.io.File object any more or can we consider it deprecated?
I believe a java.nio.file.Path can do everything a java.io.File can do and more.
![]()
8 Answers 8
Long story short:
java.io.File will most likely never be deprecated / unsupported. That said, java.nio.file.Path is part of the more modern java.nio.file lib, and does everything java.io.File can, but generally in a better way, and more.
For new projects, use Path .
And if you ever need a File object for legacy, just call Path#toFile()
Migrating from File to Path
can we consider it deprecated?
No, you can’t consider it deprecated unless and until it is so marked in the File Javadoc.
Basically file.Path will be the way to go from now on but as is widely known Java people tend to keep back-compatibility so I guess that’s why they have left it.
I will complete the very good answer of @mmcrae .
is there any reason to use a java.io.File object any more or can we consider it deprecated?
JDK classes are very rarely deprecated.
You can see on the the JDK 8 API deprecates list all classes deprecated since the first JDK.
It contains only a little part of classes that the Oracle documentation and the Java community discourage to use.
java.util.Date , java.util.Vector , java.util.Hashtable . that are classes with so many defects are not deprecated.
But why ?
Because conceptually something of deprecated means still there but discourage to use as it will very certainly be removed.
Thousands of programs rely on these bad designed classes.
For such classes, Java API developers will not give such a signal.
Answer of @EJP is so really right :
Not unless and until it is so marked in the Javadoc.
So, I think that your question would make more sense in its terms :
«As we have the choice, should we use java.io.File or java.nio.file.Path for new developments and if the answer is java.nio.file.Path , could you easily take advantage of java.io.File for legacy projects using java.io.File ?»
I believe a java.nio.file.Path can do everything a java.io.File can do and more.
You have the answer.
This oracle tutorial about legacy IO confirms your thinking.
Prior to the Java SE 7 release, the java.io.File class was the mechanism used for file I/O, but it had several drawbacks.
Many methods didn’t throw exceptions when they failed, so it was impossible to obtain a useful error message. For example, if a file deletion failed, the program would receive a «delete fail» but wouldn’t know if it was because the file didn’t exist, the user didn’t have permissions, or there was some other problem.
The rename method didn’t work consistently across platforms. There was no real support for symbolic links.
More support for metadata was desired, such as file permissions, file owner, and other security attributes.
Accessing file metadata was inefficient.
Many of the File methods didn’t scale. Requesting a large directory listing over a server could result in a hang. Large directories could also cause memory resource problems, resulting in a denial of service.
It was not possible to write reliable code that could recursively walk a file tree and respond appropriately if there were circular symbolic links.
With so many drawbacks for java.io.File , we need really no reason to use this class for new developments.
And even for legacy code using java.io.File , Oracle gives hints to use Path .
Perhaps you have legacy code that uses java.io.File and would like to take advantage of the java.nio.file.Path functionality with minimal impact to your code.
The java.io.File class provides the toPath method, which converts an old style File instance to a java.nio.file.Path instance, as follows:
You can then take advantage of the rich feature set available to the Path class.
For example, assume you had some code that deleted a file:
В чем отличия java io file и java nio file path
Let’s start with the «old» class, java.io.File . A file object can represent a filename, a directory name, a relative file or directory path, or an absolute file or directory path (where a file/directory name is actually also a relative file/directory path, relative to the directory in which the file/directory is located).
java.io.file: File and directory names
Representing file names with java.io.File
You can define a filename (without specifying a directory) as follows (we stick to the pattern «test<timestamp>» used above):
You can now read the following information from the File object:
| Method | Return value |
|---|---|
| file.getName() | test1578953190701 |
| file.getPath() | test1578953190701 |
| file.getParent() / file.getParentFile() | null |
| file.getAbsolutePath() / file.getAbsoluteFile() | /happycoders/git/filedemo/test1578953190701 |
The method getName() returns the filename we passed to the constructor. getPath() returns the path, which in this case corresponds to the filename since we have not specified a directory. For the same reason, getParent() and getParentFile() both return null , the first method returns a String, the second a corresponding File object.
Using the methods getAbsolutPath() and getAbsolutFile() , you map this file into the current working directory and get the complete path of the file including its directory and filename. These two methods also differ only in that the first returns a String, and the second returns a corresponding File object.
There are also the methods getCanonicalPath() and getCanonicalFile() , which would return the same values as getAbsolutePath() and getAbsoluteFile() in this and the following examples. We will see in a later example, in which cases they can contain other values.
Representing directory names with java.io.File
The File object constructed in the previous section could just as well represent a directory with the same name instead of a file. The methods listed in the table above would return the same results.
A distinction would only be possible if a file or directory with this name already exists. If a corresponding file exists, the method file.isFile() returns true . If, on the other hand, a directory with this name exists, the method file.isDirectory() returns true . If neither file nor directory exists, both methods return false . Depending on the further use, the File object can then be used either to create a directory or to create a file.
java.io.File: Relative file and directory paths
Relative file path with java.io.File
To specify a directory, we can pass it to the File constructor as a parameter – in the simplest form as a String. With the following code, you put the test file into a tests directory:
The getters now provide the following information about the File object (with the differences to the previous example highlighted in bold):
| Method | Return value |
|---|---|
| file.getName() | test1578953190701 |
| file.getPath() | tests/test1578953190701 |
| file.getParent() / file.getParentFile() | tests |
| file.getAbsolutePath() / file.getAbsoluteFile() | /happycoders/git/filedemo/tests/test1578953190701 |
We can now see a difference between getName() and getPath() : the first method returns only the file name without the directory information, the second method returns the complete relative path. getParent() and getParentFile() (remember: the first method returns a String, the second a corresponding File object) now return the specified directory tests . In the absolute path information returned by getAbsolutePath() and getAbsoluteFile() (again: String vs. File ), the subdirectory tests gets inserted accordingly.
The directory can also be passed as a File object instead of a String:
Relative file path with nested directories
Several directory levels can also be nested:
This allows us to construct directory paths of any depth without having to use the separator character even once. The example constructs a file object with the path tests/2020/2020-01-13/test1578953190701 .
What will the getParent() method return now? tests/2020/2020-01-13 or just 2020-01-13 ? Let’s try it…
| Method | Return value |
|---|---|
| file.getName() | test1578953190701 |
| file.getPath() | tests/2020/2020-01-13/test1578953190701 |
| file.getParent() / file.getParentFile() | tests/2020/2020-01-13 |
| file.getAbsolutePath() / file.getAbsoluteFile() | /happycoders/git/filedemo/tests/2020/2020-01-13/test1578953190701 |
The parent, therefore, represents the path to the parent directory, not just its name. To access the name of the parent directory we can use file.getParentFile().getName() .
Relative directory path with java.io.File
Also, in the examples from the previous section, test1578953190701 could be a directory instead of a file. The path does not allow any conclusions to be drawn.
java.io.File: Absolute file and directory paths
Absolute directory path with java.io.File
Attention: We look at absolute paths in reverse order: We first construct the directory path and then the file path, since we cannot generate an absolute file path without an absolute directory path as a parent.
(There is one exception, which we have already seen in the previous examples: We can obtain the absolute file path for a file in the current directory by invoking the File constructor with only the file name and then calling the method getAbsoluteFile() / getAbsolutePath() on the created File object.)
We have the following options for constructing an absolute directory path:
- from an absolute directory path represented as a String (therefore dependent on the operating system)
- from the system properties mentioned at the beginning, like «user.home» and «java.io.tmpdir»
- from the current directory
Constructing an absolute directory path from a String
Once we have the absolute directory path in a String (for example, from a configuration file), we can pass it directly to the File constructor. The following example uses a String constant for simplicity:
For this absolute directory, the File object’s getters return the following values:
| Method | Return value |
|---|---|
| file.getName() | myapp |
| file.getPath() | /var/log/myapp |
| file.getParent() / file.getParentFile() | /var/log |
| file.getAbsolutePath() / file.getAbsoluteFile() | /var/log/myapp |
The Methods getPath() , getAbsolutePath() and getAbsoluteFile() now all return the absolute path of the directory. getParent() and getParentFile() return the absolute path of the parent directory.
Constructing an absolute directory path from system properties
Through the system properties user.home and java.io.tmpdir , you get – independent of the operating system – the user’s home directory and the temporary directory.
The System.getProperty() method finally returns the path as a String, which we can then pass to the File constructor. Above, we saw that on Windows, the temporary directory has a trailing backslash, the home directory does not. Does that cause us problems at this point?
We can use the following code to test it on Windows:
The program delivers the following output:
The unnecessary trailing backslash has been removed, so we don’t have to worry about anything.
Creating an absolute directory path from the current directory
Using what we know so far, we could construct the absolute directory path of the current directory as follows:
Here we have generated a dummy file path, constructed the absolute path for it (which maps the file into the current directory), and then extracted the parent – the absolute path of the directory in which the file is located.
Admittedly, this is rather cumbersome. Fortunately, there is a more elegant way: The current directory can also be read from a system property, user.dir , and then passed to the File constructor:
Absolute file path with java.io.File
After having looked at different ways of creating an absolute directory path, we can now construct an absolute file path. All we have to do is pass the directory and filename to the File constructor.
For this example, the getters of the File object return the following values:
| Method | Return value |
|---|---|
| file.getName() | foo |
| file.getPath() | /tmp/myapp/foo |
| file.getParent() / file.getParentFile() | /tmp/myapp |
| file.getAbsolutePath() / file.getAbsoluteFile() | /tmp/myapp/foo |
We see a similar pattern as in the example with the absolute directory path /var/log/myapp : The methods getPath() , getAbsolutePath() and getAbsoluteFile() return the absolute path of the file. And getParent() and getParentFile() return the absolute path of the parent directory.
Attention:
If you create a file object for a file in the current directory, it makes a difference whether you create the object with the current directory and filename or just the filename. The methods getAbsoluteFile() / getAbsolutePath() return the absolute file path in both cases. However, getPath() returns the absolute path only in the first case; whereas in the second case it returns only the filename. And getParent() and getParentFile() only return the parent directory in the first case, but null in the second case.
java.io.file: Overview of File’s getter methods
The following table summarizes what File ‘s getters return depending on the file system object represented:
| Method | File/directory name | Relative file/directory path | Absolute file/directory path |
|---|---|---|---|
| getName() | File/directory name | File/directory name | File/directory name |
| getPath() | File/directory name | Relative file/directory path | Absolute file/directory path |
| getParent() / getParentFile() | null | Relative path to parent directory | Absolute path to parent directory |
| getAbsolutePath() / getAbsoluteFile() | Absolute path from combination of current directory and file/directory name | Absolute path from combination of current directory and relative file/directory path | Absolute file/directory path |
Once again as a reminder: getParent() and getAbsolutePath() return a String; getParentFile() and getAbsoluteFile() return a corresponding File object.
java.io.file: What is the difference between getCanonicalPath() / getCanonicalFile() and getAbsolutePath() / getAbsolutePath()?
In all previous examples, the methods getCanonicalPath() and getCanonicalFile() would have returned the same result as getAbsolutePath() and getAbsoluteFile() – namely the respective absolute path (as String or File object).
So what is the difference?
A «canonical path» is unique; i.e., there is only one such path to a file. In contrast, there can be more than one absolute path to the same file. An example:
For the file /var/log/syslog , this String is also the «canonical path». The same String is also an absolute path. However, there are other absolute paths, such as
- /var/log/./syslog ,
- /var/log/../log/syslog ,
- /home/user/../../var/log/syslog ,
- as well as all paths that eventually point to /var/log/syslog via symbolic links.
Constructing file and directory paths with java.nio.file.Path and Paths
Although the interface of java.nio.file.Path has been completely changed from java.io.File , the underlying concepts have remained unchanged. Filenames, directory names, relative file and directory paths, and absolute file and directory paths are still the file system objects being represented.
What was changed? In the following sections, you see how to construct Path objects, using the same structure and the same examples as with the File objects before.
In the following sections, I will not make a distinction between file and directory names. We have already seen above that, as long as the corresponding file system object does not exist, its path does not indicate whether it is a file or a directory.
java.nio.file.Path: File and directory names
Instead of a constructor, we use a factory method for java.nio.file.Path to create instances. The factory method was originally located in the class java.nio.file.Paths (with «s» at the end), but in Java 11, it was also directly included in the Path class. We create a Path object for the file name «test<timestamp>» as follows:
Starting with Java 11, you can use Path.of() instead of Paths.get() . There is practically no difference. Internally, Paths.get() calls the newer method Path.of() and this in turn calls FileSystems.getDefault().getPath() .
The following methods provide information analogous to methods of the File class shown above:
| Method | Return value |
|---|---|
| path.getFileName() | test1579037366379 |
| path.getNameCount() | 1 |
| path.getName(0) | test1579037366379 |
| path.getParent() | null |
| path.getRoot() | null |
| path.toAbsolutePath() | /happycoders/git/filedemo/test1579037366379 |
| path.normalize() | test1579037366379 |
First of all: All methods shown – except for getNameCount() – return a Path object. There are no variants of these methods that return a String. The only way to convert a Path object into a String is to call its toString() method.
The getFileName() method returns the name of the file or directory. getNameCount() is always 1 for a filename without directory information, or for directories without a parent directory. And the first name, retrievable by getName(0) , is also the file/directory name. getParent() returns – like File ‘s method with the same name – null . The method getRoot() also returns null , since a single filename is always relative. With toAbsolutePath() we map – analogous to File.getAbsolutePath() – the file/directory into the current directory and get the corresponding absolute path.
Path.normalize() is similar to File.getCanonicalPath() with the difference that when normalizing a path, relative paths remain relative, while getCanonicalPath() always generates an absolute path for both absolute and relative File objects.
java.nio.file.Path: Relative file and directory paths
To include directories, Path offers two different approaches:
- You can list all directories in the factory method Paths.get() or Path.of() .
- You can use its resolve() method to convert an existing Path object into a new Path object, where the resolve() method is equivalent to changing the directory with the Windows or Linux cd command.
Constructing relative file and directory paths with Paths.get() / Path.of()
You can pass any number of directory names to the factory methods. For example, you would construct the relative path tests/test1578953190701 as follows (the timestamp is hard-coded for simplicity):
This Path object’s getters return the following results (I highlighted the differences to the individual file names in bold again):
| Method | Return value |
|---|---|
| path.getFileName() | test1578953190701 |
| path.getNameCount() | 2 |
| path.getName(0) | tests |
| path.getName(1) | test1578953190701 |
| path.getParent() | tests |
| path.getRoot() | null |
| path.toAbsolutePath() | /happycoders/git/filedemo/tests/test1578953190701 |
| path.normalize() | tests/test1578953190701 |
Due to the directory, nameCount has increased from 1 to 2. In the name array, the directory name was inserted at position 0; the file name has moved to position 1. getParent() also returns the directory name. The absolute path and the normalized version also contain the directory name.
Constructing nested file and directory paths with Paths.get() / Path.of()
If the file is to be located in the directory tests/2020/2020-01-13 , call the factory method as follows:
Here once again, the getters’ results:
| Method | Return value |
|---|---|
| path.getFileName() | test1578953190701 |
| path.getNameCount() | 4 |
| path.getName(0) | tests |
| path.getName(1) | 2020 |
| path.getName(2) | 2020-01-13 |
| path.getName(3) | test1578953190701 |
| path.getParent() | tests/2020/2020-01-13 |
| path.getRoot() | null |
| path.toAbsolutePath() | /happycoders/git/filedemo/tests/2020/2020-01-13/test1578953190701 |
| path.normalize() | tests/2020/2020-01-13/test1578953190701 |
nameCount has been increased accordingly, and the name array contains all directories of the path as well as the filename. Again, getParent() returns the complete known path to the directory, not just its name.
Constructing relative file and directory paths with Path.resolve()
An alternative way to construct the Path object for the path tests/2020/2020-01-13/test1578953190701 is to use the resolve() method. It combines the path on which the method is called with the path passed to it:
The resolve() operation is associative, i.e., the individual parts can also be joined in a different order, for example like this:
Shortcut: Path.resolveSibling()
Let’s assume we have the Path object we constructed in the previous section, and we want to create another file in the same directory. If we still have access to the Path object representing the directory, we can call resolve() on it with the new filename. Otherwise, we could access the directory with getParent() and then call resolve() :
Exactly for this purpose, there is the shortcut resolveSibling() , which saves you five keystrokes and bytes:
java.nio.file.Path: Absolute file and directory paths
Similar to java.io.File , we can also create an absolute path with java.nio.file.Path from a String that we read from a configuration file or a system property. We can pass this path, as it is, to the factory method. We do not need to split it up first. Using the directory / var/log/myapp as an example again:
Of course, we can also modify an absolute path using the resolve() method. The following example corresponds to the example of the absolute file path with java.io.File :
For this absolute path, the getter methods return:
| Method | Return value |
|---|---|
| path.getFileName() | foo |
| path.getNameCount() | 3 |
| path.getName(0) | tmp |
| path.getName(1) | myapp |
| path.getName(2) | foo |
| path.getParent() | /tmp/myapp |
| path.getRoot() | / |
| path.toAbsolutePath() | /tmp/myapp/foo |
| path.normalize() | /tmp/myapp/foo |
Here we experience for the first time that path.getRoot() does not return null, but «/», the Linux root directory. On Windows, we would get «C:\» here (unless the temporary directory is in a different file system). The root directory is not part of the name array.
java.nio.file.Path: Overview of its getter methods
Here you can see a summary of what Path ‘s getters return:
| Method | File/directory name | Relative file/directory path | Absolute file/directory path |
|---|---|---|---|
| path.getFileName() | File/directory name | File/directory name | File/directory name |
| path.getNameCount() | 1 | Number of directories + file | Number of directories + file |
| path.getName(index) | File/directory name | File/directory names at the given position (0-based) | File/directory names at the given position (0-based, root does not count) |
| path.getParent() | null | Relative path to parent directory | Absolute path to parent directory |
| path.getRoot() | null | null | The file system’s root, such as «/» or «C:\» |
| path.toAbsolutePath() | Absolute path from combination of current directory and file/directory name | Absolute path from combination of current directory and file/directory name | Absolute file/directory path |
| path.normalize() | Normalized file/directory name | Normalized relative file/directory name | Normalized absolute file/directory name |
Summary and outlook
This article gave a detailed overview of how to construct file and directory paths independent of the operating system using the classes File , Path , and Paths .
My recommendation is to use only the NIO.2 classes Path and Paths from Java 7 on, and from Java 11 on, use only Path . If you need a File object for a particular file operation, you can always create one with Path.toFile() .
The following parts of the series will deal with the following topics:
In the later course we come to advanced topics:
-
introduced in Java 1.4, to speed up working with large files for blazing-fast file access without streams , to access the same files in parallel – i.e., from several threads or processes – without conflicts
Do you have any questions, suggestions, ideas for improvement? Then I would be pleased with a comment. Do you know others who find the topic interesting? Then I would be happy if you share the article by using one of the buttons below. Would you like to be informed when the next part appears? Then click here to sign up for the HappyCoders newsletter.