FileNotFoundException in Java
Let’s take a look at FileNotFoundException
, a common Java exception that needs to be handled when a Java application reads from or writes to files.
FileNotFoundException: quick facts
- Qualified class name:
java.io.FileNotFoundException
. - Exception type: checked exception.
- Summary: signals failure to open a file because the file doesn’t exist or permissions are insufficient.
- Subclass of:
java.io.IOException
.
When and why does FileNotFoundException usually occur in Java?
FileNotFoundException
gets thrown when a Java application is unable to open a file at the path that it’s given. This can happen for a few reasons:
- The specified file does not exist.
- The specified file is in fact a directory.
- The specified file exists but the Java application can’t access it due to insufficient permissions.
FileNotFoundException
is usually thrown when you pass a File
object to an object of type FileInputStream
, Scanner
or FileReader
(used to read from files), FileOutputStream
(used to write to files), or RandomAccessFile
(can both read and write).
Is FileNotFoundException a checked or unchecked exception?
FileNotFoundException
is a checked exception, which means that the Java compiler requires you to handle it. Any time you use a method that can potentially throw FileNotFoundException
, you need to either:
- Handle this exception using a
try/catch
block, or - Add a
throws
clause to your method to delegate the responsibility of handling this exception to callers of your method.
If you don’t do any of this, Java will prevent you from compiling your application.
What Java exceptions are related to FileNotFoundException?
FileNotFoundException
extends IOException
which, in turn, is a subtype of Exception
, the base type of all checked exceptions in Java. Here’s the class hierarchy for FileNotFoundException
:
Throwable (java.lang)
Exception (java.lang)
IOException (java.io)
FileNotFoundException (java.io)
IOException
is the most generic exception in a large group of exceptions that express input/output and networking errors in Java applications. Other subtypes of IOException
besides FileNotFoundException
include EOFException
, MalformedInputException
, FileSystemException
, MalformedURLException
, UnknownHostException
, and many others.
FileNotFoundException example: using a FileReader
Since FileNotFoundException
is a checked exception, the Java compiler won’t let you get away with not handling it. For example, what happens if you try to compile the code below?
private static void tryReadFile() {
File myFile = new File("file.txt");
FileReader reader = new FileReader(myFile);
}
Compilation will fail with the following error message:
../FileAccessSample.java:14:29
java: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
That’s because FileReader
uses the throws
clause to delegate handling of FileNotFoundException
to its callers:
public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file));
What do you do about it? Let’s see how you can catch this exception, or even a group of related exceptions, right in this method, or use the throws
clause to do it elsewhere.
How to catch FileNotFoundException
To address the compilation error shown above, you need to either put the FileReader
call into a try/catch
block or include a throws
clause in your method declaration. Let’s consider the two scenarios.
Handling FileNotFoundException with the throws clause
One way to solve the compilation error shown above would be to add a throws
clause to the method:
private static void tryReadFile() throws FileNotFoundException {
File myFile = new File("file.txt");
FileReader reader = new FileReader(myFile);
}
(Notice that since FileNotFoundException
derives from IOException
, a throws IOException
declaration will also work.)
Doing so would make callers of this method responsible for handling the FileNotFoundException
. That is, unless this method is unused, trying to compile again will result in a similar compiler error that relocates to wherever you’re calling your method from:
../FileAccessSample.java:9:56
java: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
You could then add a similar throws
clause to the calling method, then to its calling method, and continue doing so all the way up to your application’s main()
method. If the main()
method gets FileNotFoundException
added to its throws
clause as well, the compiler will give up and let you build the application. However, as soon as the exception actually occurs, your application will simply exit without a chance of recovery if the file isn’t there. Technically, you can do this, but leaving your application without error handling code is a really bad idea.
Only use the throws
clause when you have a strategy of handling exceptions up the call stack in your application. If you’re writing a library, use the throws
clause if you believe that its users will benefit from flexibility in handling error conditions more than they will struggle from having to write extra exception handling code.
In Java frameworks such as Spring REST services, you can use the throws
clause to propagate FileNotFoundException
and other exceptions all the way to the REST controller and handle them there, transforming internal exception details to clear error messages that API consumers can understand.
Handling FileNotFoundException using the try/catch block
If you want to handle FileNotFoundException
right there in your method, you need to put the FileReader
constructor into a try/catch
block:
private static void tryReadFile() {
File myFile = new File("file.txt");
try {
FileReader reader = new FileReader(myFile);
} catch (FileNotFoundException exception) {
// Exception handling code goes here
}
}
How exactly you handle FileNotFoundException
here may vary. For a console application, it may be enough to simply print the exception’s stack trace:
catch (FileNotFoundException exception) {
e.printStackTrace();
}
If you’re logging errors instead of relying on console output, you can log the exception and specify relevant details, such as the path to the file that your application failed to read:
catch (FileNotFoundException exception) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Could not read file {0}. {1}.",
myFile.getAbsolutePath(),
myFile.exists()
? "The file is reported as existing, check access permissions"
: "The file does not exist"),
exception);
}
Alternatively, you could recover by creating a new file:
catch (FileNotFoundException exception) {
try {
myFile.createNewFile();
} catch (IOException ioException) {
throw new RuntimeException(MessageFormat.format("Unable to create file {0}.",
myFile.getAbsolutePath()),
ioException);
}
}
Instead of only creating a new file when you handle FileNotFoundException
, you could as well check that the file exists in the try
part of the block:
private static void tryReadFile() {
File myFile = new File("file.txt");
try {
if (!myFile.exists()) {
myFile.createNewFile();
myFile = addDefaultContent(myFile);
}
FileReader reader = new FileReader(myFile);
} catch (IOException exception) {
LOGGER.log(Level.SEVERE,
MessageFormat.format("Could not read or create file {0}.",
myFile.getAbsolutePath()),
exception);
}
}
Notice that in this case, the catch
block targets IOException
, and that’s due to inheritance:
createNewFile()
throws anIOException
.new FileReader()
throws aFileNotFoundException
, which is a subtype ofIOException
.
The Java compiler wants you to catch both of these checked exceptions, but since IOException
is the parent of FileNotFoundException
, you only need to catch IOException
, which will also handle FileNotFoundException
using the same exception handling code.
In case you want to handle IOException
and FileNotFoundException
in two different ways, you can add two catch
blocks. The first block should target the more specific FileNotFoundException
and the second block should target the more generic IOException
:
try {
// Try do something
} catch (FileNotFoundException exception) {
// FileNotFoundException is handled first.
}
catch (IOException exception) {
// IOException is handled second.
}
If you attempt to catch the more generic IOException
in the first block, it will also catch FileNotFoundException
, and the second catch
block will become unused, causing a compiler error:
../FileAccessSample.java:94:9
java: exception java.io.FileNotFoundException has already been caught
One more thing to know about handling FileNotFoundException
is that as you move your code around, you may end up catching this exception where it can no longer occur. If this happens, you’ll see a compiler error:
../FileAccessSample.java:104:11
java: exception java.io.FileNotFoundException is never thrown in body of corresponding try statement
Here’s an example of code that can cause this compiler error:
private static void tryReadFile() {
File myFile = new File("existingFile.txt");
try {
FileReader reader = getFileReader(myFile);
BufferedReader bufferedReader = new BufferedReader(reader);
System.out.println(bufferedReader.lines()
.collect(Collectors.joining(System.lineSeparator())));
} catch (FileNotFoundException exception) {
// Exception handling code goes here
}
}
Instead of instantiating a FileReader
object right here in the try
block, this code calls getFileReader(myFile)
to get the object elsewhere. As a result, a FileNotFoundException
can only occur (and should be handled) inside getFileReader()
, but not in the method that makes a call to it. In this case, you should remove the catch
block targeting FileNotFoundException
that the compiler complains about.
How to throw FileNotFoundException
You rarely want to throw a FileNotFoundException
, but if you do, you can use one of the following constructors:
- A parameterless constructor:
throw new FileNotFoundException();
- A constructor that lets you specify the reason for throwing the exception:
throw new FileNotFoundException("Here's what happened here");
Since it’s always wise to provide details for troubleshooting, using the latter constructor is the better choice.
Unlike many other Java exceptions, FileNotFoundException
doesn’t provide a constructor that would allow passing a different exception as the cause.
Production debugging isn’t scary with Lightrun
Properly handling exceptions is one thing that you as a developer can do to ensure a smooth ride in production for your Java applications. Still, let’s face it: any non-trivial application will have bugs. You’re lucky if you can reproduce a bug in a local environment, debug and happily push a verified fix.
What if you can’t? Debugging remotely is tricky: you need to rely on existing logging, repeatedly redeploy updates with more logs and attempted fixes, and you’re even unable to set a proper breakpoint because you can’t afford to halt a production environment.
Take a look at Lightrun: our next-gen remote debugger for your production environment. With Lightrun, you can inject logs without changing code or redeploying, and add snapshots: breakpoints that don’t stop your production application. Lightrun supports Java, .NET, Python and Node.js applications, integrates with IntelliJ IDEA and VS Code. Create a Lightrun account and check for yourself!
It’s Really not that Complicated.
You can actually understand what’s going on inside your live applications.