Java Files
It is often the case that a program needs to process and store data located outside the codebase: configuration settings, some datasets for processing, logs of execution, and so on. The simplest way to store data is to use files, which are supported by all modern operating systems. You can consider a file as a collection of data that is stored on a disk or another device, and that can be manipulated as a single unit when addressed by its name. Files can be organized into directories that act as folders for other files and directories.
In this topic, we will learn how to work with files directly from a Java program.
The File class
There is a class called File
in the java.io
package. An object of this class represents an existing or non-existing file or a directory. The class can be used for basic file operations: creating, removing, accessing properties and more.
The simplest way to create a file object is to pass a string path to its constructor. The valid format of the string depends on the operating system:
- Windows uses backslashes for paths (
'\'
), - Linux, macOS, Android, and other UNIX-like systems use the forward slash (
'/'
).
You should keep this difference in mind while working with files.
If your operating system is Windows, do not forget to use the escape character '\'
.
Let's create two objects of the File
class for different platforms.
File fileOnUnix = new File("/home/username/Documents"); // a directory on a UNIX-like system
File fileOnWin = new File("D:\\Materials\\java-materials.pdf"); // a file on Windows
The code will work even if a file or a directory does not actually exist in your file system. It does not create a new file or directory. It just represents "a virtual" file or directory that exists already or may be created in the future.
To display the character for separating the path to a file in your case, you can print the following:
System.out.println(File.separator); // '/' - for Linux
Objects of the File
class are immutable; that is, once created, the abstract pathname represented by an object will never change.
Absolute and relative path
You've already seen examples of files described by an absolute path. Simply, a path is absolute if it starts with the root element of the file system. It has the complete information about the file location including the type of the operating system.
It is considered bad practice to locate a file using its absolute path inside your programs, since you will lose the ability to reuse your program on different platforms. Another problem is that you cannot transfer the file along with the specified directory, you will have to change the code that accesses it.
A relative path is a path that doesn't include the root element of the file system. This always starts from your working directory. This directory is represented by a .
(dot). A relative path is not complete and needs to be combined with the current directory path in order to reach the requested file.
Here is an example with a file inside the images directory which is in your working directory:
File fileOnUnix = new File("./images/picture.jpg");
File fileOnWin = new File(".\\images\\picture.jpg");
As you can see, both paths look exactly the same, which provides platform independence. Interestingly, the dot character can be skipped, so the path images/picture.jpg
is also correct.
In order to construct platform-independent programs, it is a common convention to use relative paths whenever possible. You can also transfer the working directory that contains images/picture.jpg
without any code modifications.
To access the parent directory, just write ..
(double dot). So, ../picture.jpg
is a file placed in the parent directory of the working directory. The relative path images/../images/picture.jpg
means the parent directory of images
, then the images
folder again. And picture.jpg
is the file inside images
folder. In general images/../images/picture.jpg
and images/picture.jpg
are the same paths.
Basic methods
An instance of File
has a list of methods. Take a look at some of them:
String getPath()
returns the string path to this file or directory;String getName()
returns the name of this file or directory (just the last name of the path)boolean isDirectory()
returnstrue
if it is a directory and exists, otherwise,false
;boolean isFile()
returnstrue
if it is a file that exists (not a directory), otherwise,false
;boolean exists()
returnstrue
if this file or directory actually exists in your file system, otherwise,false
;String getParent()
returns the string path to the parent directory that contains this file or directory.
The list is not complete, but for now, we will focus on these ones. For other methods, see here.
Let's create an instance of an existing file and print out some info about it.
File file = new File("/home/username/Documents/javamaterials.pdf");
System.out.println("File name: " + file.getName());
System.out.println("File path: " + file.getPath());
System.out.println("Is file: " + file.isFile());
System.out.println("Is directory: " + file.isDirectory());
System.out.println("Exists: " + file.exists());
System.out.println("Parent path: " + file.getParent());
As we expect, the code prints the following:
File name: javamaterials.pdf
File path: /home/username/Documents/javamaterials.pdf
Is file: true
Is directory: false
Exists: true
Parent path: /home/username/Documents
Suppose now we have an instance that represents a non-existing file and prints the details about it:
File name: javamaterials1.pdf
File path: /home/art/Documents/javamaterials1.pdf
Is file: false
Is directory: false
Exists: false
Parent path: /home/art/Documents
The file does not exist, so the application does not know its type.
There is also a group of methods; canRead()
, canWrite()
, and canExecute()
, to test whether the application can read/modify/execute the file denoted by the path. It is recommended to use these methods, otherwise, you can encounter file access errors when your user does not have enough permissions to perform a operation with a file.
We believe Java File
class provides a very clear API to process files and directories on different platforms.
Managing files
As you already know, the java.io.File
represents an abstract path to a file or a directory that may not even exist. Apart from just traversing the file hierarchy, we are also able to manage files and directories by creating, deleting and renaming them. Let's consider several methods for doing it. You are able to use considered methods in different operating systems, but in the following example, we work with UNIX-like OS.
Creating files
To create a file in the file system, we'll have to do the following:
- Create an instance of
java.io.File
with the specified abstract path. - Invoke the
createNewFile
method of this instance.
After creating an instance of File
we should invoke the method — createNewFile
. The method returns true
if the file was successfully created and false
if it already exists. It does not erase the content of an existing file.
File file = new File("/home/username/Documents/file.txt");
try {
boolean createdNew = file.createNewFile();
if (createdNew) {
System.out.println("The file was successfully created.");
} else {
System.out.println("The file already exists.");
}
} catch (IOException e) {
System.out.println("Cannot create the file: " + file.getPath());
}
Try to play with this code yourself for better understanding.
You may ask: "why does the method return false instead of throwing an exception when the file already exists"? The answer is that sometimes it does not matter for the program if the file was created right now or already existed.
Creating directories
To create a directory we also need to start by creating an instance of java.io.File
. After that, we should call one of the two methods of this instance:
boolean mkdir
creates the directory. It returnstrue
only if the directory was created, otherwise, it returnsfalse
.boolean mkdirs
creates the directory including all necessary non-existing parent directories. It returnstrue
only if the directory was created along with all of the specified parent directories.
Both methods do not throw IOException
, unlike the createNewFile
method.
The following example demonstrates the mkdir
method.
File file = new File("/home/art/Documents/dir");
boolean createdNewDirectory = file.mkdir();
if (createdNewDirectory) {
System.out.println("It was successfully created.");
} else {
System.out.println("It was not created.");
}
We recommend you try this code on your computer specifying different paths to the file.
Generally, the code works as follows: if the directory does not exist, this code creates it. If the directory exists, the code will not create it. If there is a non-existing parent directory in the path, the directory also will not be created. No exceptions occur in any case.
Here is another example, demonstrating the mkdirs
method. It creates the target directory and all parent directories if they do not exist.
File file = new File("/home/art/Documents/dir/dir/dir");
boolean createdNewDirectory = file.mkdirs();
if (createdNewDirectory) {
System.out.println("It was successfully created.");
} else {
System.out.println("It was not created.");
}
The boolean variable is true
, if the target directory was created, regardless of the existence of the parent directories.
Removing files and directories
Now that we know how to create a directory, let's find out how to get rid of one. There is a method named delete
to remove a file or a directory. It returns true
if and only if the file or directory is successfully deleted, otherwise, it returns false
. The method returns false
if the file or directory does not exist. It is important to remember that it also returns false
if the directory contains subdirectories or files. It means that the method will not remove a hierarchy, only a particular file or an empty directory.
File file = new File("/home/art/Documents/dir/dir/dir");
if (file.delete()) {
System.out.println("It was successfully removed.");
} else {
System.out.println("It was not removed.");
}
In cases where you want to delete a non-empty directory, there is an option to first delete all the nested files and directories. Take a look at the code below. It recursively deletes directories with their content. Note that the method assumes that the passed directory dir
does exist. Otherwise, children
array will be empty and NullPointerException
will be thrown.
public void deleteDirRecursively(File dir) {
File[] children = dir.listFiles();
for (File child : children) {
if (child.isDirectory()) {
deleteDirRecursively(child);
} else {
child.delete();
}
}
dir.delete();
}
The delete
method never throws an IOException
.
There's also another method for removing files. It is called deleteOnExit
and it removes a file or a directory when your program stops. Note that once deletion has been requested, there is no way to cancel it.
Renaming and moving files and directories
Now, let's take a look at renaming files and directories.
The method renameTo
changes the name of the file by editing it in the abstract path. It returns true
if and only if the renaming succeeds, otherwise, false
.
File file = new File("/home/art/Documents/dir/filename.txt");
boolean renamed = file.renameTo(new File("/home/art/Documents/dir/newname.txt"));
The same method can be used to move the file or directory from the current location to another one:
File file = new File("/home/art/Documents/dir/file.txt");
boolean renamed = file.renameTo(new File("/home/art/Documents/another/file.txt"));
Many aspects of this method's behavior remain platform-dependent. It might not be able to move a file from one filesystem to another and it might not succeed if a file with the same destination already exists. The return value should always be checked to make sure that the operation was successful.
File file = new File("/home/art/Documents/dir/filename.txt");
File renamedFile = new File("/home/art/Documents/dir/newname.txt");
boolean renamed = file.renameTo(renamedFile);
if (renamed) {
System.out.println("It was successfully renamed.");
} else {
System.out.println("It was not renamed.");
}
The method renameTo
throws NullPointerException
in case the destination file is null.
Most of the considered methods return false
in case you don't have the permission to perform a corresponding operation: rename, move or remove files and directories. However, create method throws the java.io.IOException
in similar cases.
Conclusion
We have considered a set of methods to manage files and directories: creating, deleting and renaming. These methods return a boolean value that depends on the success of the operation. Now you can manage files in the file system using Java programs!