Checked vs. Unchecked Exceptions in Java
Overview
1. Introduction
In this tutorial, we'll look at the differences between checked and unchecked exceptions and when to use each one.
2. What is a Checked Exception
In Java, a checked exception is a recoverable error that the application's programmer should anticipate. A recoverable error means something that we can try a second strategy after an unsuccessful first try. For instance, if we try to open a file that doesn't exist, we can recover using a default file, creating the file, or throwing a different error.
Another marker of checked exceptions is that the programmer should anticipate them in compile time. Unlike unchecked exceptions, which we'll see shortly, checked exceptions should be handled or declared. At compile time, we should either use a catch block to handle the exception properly or declare it in the method signature using the throws keyword.
Finally, another technical aspect of checked exceptions is that they inherit from the Exception class but not from the RuntimeException class. We'll see the complete Exception hierarchy in Section 5.
2.1. How to Handle a Checked Exception
We can handle checked exceptions by treating the exception using a try/catch clause or declaring throws in the method signature. Let's first look at the try/catch way in the following code:
1private void processFile(String path) {
2 try {
3 FileInputStream fis = new FileInputStream(new File(path));
4 } catch (FileNotFoundException e) {
5 //treat the exception properly
6 }
7}
The FileInputStream class constructor throws the checked FileNotFoundException if the file doesn't exist. Thus, we should anticipate the exception at compile time. In the above example, we've used a try/catch block to handle the exception.
Now, let's examine how the throws keyword works in the code below:
1private void processFile(String path) throws FileNotFoundException {
2 FileInputStream fis = new FileInputStream(new File(path));
3}
By using throws, the application transmits the FileNotFoundException to the method that calls processFile. Therefore we don't need to handle the exception here.
3. What is an Unchecked Exception
Unlike checked exceptions, unchecked exceptions are unpredictable, so the programmer doesn't need to anticipate them in code. Thus, our application doesn't need any try/catch or throws keywords around the code that potentially throws an unchecked exception.
Unchecked exceptions are subclasses of the RuntimeException class. Even though we are not obligated to handle RuntimeExceptions, it is a good practice to use either the try/catch or throws keywords to handle them in some cases.
In Java, some well-known scenarios throw unchecked exceptions, and we can predict them. For instance, the NullPointerException and ArithmeticException are known to Java programmers. The first states that we're accessing a member of a null variable. And the second says that we're doing an incorrect arithmetic operation, like dividing by zero. Both are good candidates to be anticipated by the programmer.
3.1. How to Handle an Unchecked Exception
Let's have a look at how unchecked exception works, in that case, an ArithmeticException:
1private Integer divideByZero(Integer number) {
2 return number / 0;
3}
The code above throws an exception, but the application doesn't obligate us to handle that exception. Thus, we need to inspect the division operator (/) in Java to check that it might throw an ArithmeticException. To avoid future problems, we can surround the division expression with a try/catch clause:
1private Integer divideByZero(Integer number) {
2 int result = 0;
3 try {
4 result = number / 0;
5 } catch (ArithmeticException ex) {
6 //handle the exception
7 }
8
9 return result;
10}
Note: The try/catch block has its scope, so we need to define our variables outside of it if we want to use them in the method scope.
We can also use the throws clause in the same way as with unchecked exceptions:
1private Integer divideByZero(Integer number) throws ArithmeticException{
2 return number / 0;
3}
4. Checked vs. Unchecked Exceptions
Here's what the Oracle Official Documentation suggests about when to use a checked or an unchecked exception:
"If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception."
While it is a matter of taste when to use one or another, we can follow good practices and patterns found at the JDK. In the following sections, we'll use the quote above to create a plan to use them effectively.
4.1. Class Hierarchy
Knowing the hierarchy of the error classes in Java is helpful, and a diagram is the best way to visualize it. Use the following image to reference what are the checked and unchecked exceptions:
Remember that: checked exceptions inherit from Exception but not RuntimeException, and we should handle them in compile time. In contrast, unchecked exceptions inherited from RuntimeException don't need to be handled.
4.2. Using Checked Exceptions
Let's pick some checked exceptions, like IOException, ParseException, and SQLException. All of them happen in any of the predictable and recoverable scenarios where we:
- Try to open or close a file that might not exist.
- Attempt to parse an object to another, like a date to text.
- Execute an SQL query that doesn't work well due to some connection issue, wrong query syntax, etc.
The point is that all the scenarios described are predictable by the programmer. Also, we can recover the application by retrying a query if it fails, creating the file if it doesn't exist, or trying a different parsing algorithm.
4.3. Using Unchecked Exceptions
Unchecked exceptions are something that should not happen but somehow happened. We should only use instances of RuntimeException in those cases. For example, NullPointerException is an unexpected error that sometimes appears due to the application's current state.
The controversy about checked exceptions is that handling them in compile time might create slowness in code development. However, we use APIs to follow their practices and use them properly. Let's check another statement from Oracle:
"Generally speaking, do not throw a RuntimeException or create a subclass of RuntimeException simply because you don't want to be bothered with specifying the exceptions your methods can throw."
Generally, use checked exceptions for predictable error scenarios and unchecked for unpredictable ones.
6. Conclusion
In this article, we've seen the differences between unchecked and checked exceptions, how to handle them, and when to use both.