Exceptions
The Oracle documentation defines an exception as
An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions.
When a code throws an exception, the Java runtime system will propagate or forward this exception to the caller. If the caller has a block of code to handle the exception, the handler handles it. If the caller does not have an exception handling mechanism, the exception is forwarded to its caller and so on. The runtime system does this till it finds a block of code to handle the exception. If the exception is not handled, then the JVM will terminate the program. This block of code that handles an exception is called an exception handler.
In this post, we will see about checked and unchecked exceptions in Java.
Exception handler
doWork is a method that can throw and exception. The caller of the code can handle the exception by catching the code. The snippet of code within the catch block is the exception handler.
doWork() {
...
if (someCondition) {
throw new RuntimeException("Exception occurred");
}
}
try {
helper.doWork();
} catch (RuntimeException e) {
System.err.println(e.getMessage());
}
What are checked and unchecked exception
In Java, all exceptions inherit from the Throwable class. There are two subclasses of Throwable viz., Error and Exception. One of the subclasses of Exception is RuntimeException.
Checked exceptions are those exceptions that are of type Exception (class Exception and its subtypes) except RuntimeException (and its subclasses). Examples:IOException, SQLException etc.
Unchecked exceptions are those that are either Error or RuntimeException. In other words, classes Error or RuntimeException or those that inherit from Error or RuntimeException are unchecked exceptions. Examples: ClassCastException, NumberFormatException.
Checked exceptions
Checked exceptions are checked at compile time. If a method throws a checked exception, then the throws clause of the method signature must specify the exception. The code will not compile without this.
On the caller side, the method that calls a method that can throw a checked exception must either
- Handle the exception (with a catch block)
- Declare the checked exception in its throws clause (propagating it back to its caller).
Let us say we have a method that can throw a checked exception (FileNotFoundException). The method will look like
public class Worker {
/**
* Process the file.
* @param file the file.
* @throws FileNotFoundException thrown when file does not exist.
*/
public void processFile(File file) throws FileNotFoundException {
if (!file.exists()) {
throw new FileNotFoundException("File not found");
}
}
}
Note, as stated earlier, adding the exception to the throws clause is mandatory. Let us look at the caller of this code when it handles the exception and when it just propagates it back
Handling exception
The caller handling the checked exception will look like
public class Caller {
public void someMethod() {
Worker worker = new Worker();
File file = new File("..");
try {
worker.processFile(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
//handle the file not found
}
}
}
The code for handling the exception could have logic to recover from the exception. In this case, if the file is not found, it can re-create the file and call processFile again.
Adding to method signature
public class Caller {
public void someMethod() throws FileNotFoundException {
Worker worker = new Worker();
File file = new File("..");
worker.processFile(file);
}
}
In this, the caller decides not to handle the exception. Instead, it leaves it to its caller. It can in turn either handle it or re-throw it back and so on. But, some code in the call chain must handle it. Otherwise, the program will terminate with the thrown exception.
To summarize, the compiler forces you to handle the checked exceptions. In our example, when you just type worker.processFile(file), the compiler will complain. It will ask you to either catch the exception or re-throw it back.
Unchecked exceptions
Unchecked exceptions are not checked during compile time. Hence, the method throwing an unchecked exception will not (need not) declare it in its signature.
Example: The below method throws an IllegalArgumentException if the divisor is zero
public class Calculator {
/**
* Divides two numbers.
* @param a the dividend.
* @param b the divisor.
* @return the result of division
*/
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Divisor cannot be zero");
}
return a / b;
}
}
public class Caller {
public void someMethod() {
Calculator calculator = new Calculator();
calculator.divide(2, 0);
}
}
Running this code will result in
Exception in thread "main" java.lang.IllegalArgumentException: Divisor cannot be zero
The exception thrown was a RuntimeException. The compiler did not force us to handle it or be prepared for it. Unchecked exceptions are generally programming errors.
Note: Unchecked exceptions need not be always created and thrown. It can result from an exception at runtime. In the above calculator code, if we had not checked for the divisor being zero, it would have thrown an java.lang.ArithmeticException at runtime.
public int divide(int a, int b) {
return a / b; //Results in a RuntimeException (unchecked)
}
Conclusion
To conclude, checked exceptions are checked at compile time. The compiler will also force the caller of the method to handle it appropriately. Use a checked exception if the caller can recover from it. For instance, if the file is not found, the caller can create one and retry the call. On the other hand, unchecked exceptions are not checked at compile time. They denote programming errors. These in general should not be caught.