In Java “an event that occurs during the execution of a program that disrupts the normal flow of instructions” is called an exception. This is generally an unexpected or unwanted event which can occur either at compile-time or run-time in application code. Java exceptions can be of several types and all exception types are organized in a fundamental hierarchy.
Understanding this hierarchy is crucial for implementing robust error handling strategies in production.
Java Exceptions Hierarchy
The class at the top of the exception class hierarchy is the Throwable class, which is a direct subclass of the Object class. Throwable has two direct subclasses - Exception and Error.
The diagram below shows the standard exception and error classes defined in Java, organized in the Java exceptions hierarchy:

The Exception class is used for exception conditions that the application may need to handle. Examples of exceptions include IllegalArgumentException
, ClassNotFoundException
and NullPointerException
.
The Error class is used to indicate a more serious problem in the architecture and should not be handled in the application code. Examples of errors include InternalError
, OutOfMemoryError
and AssertionError
.
Exceptions are further subdivided into checked (compile-time) and unchecked (run-time) exceptions. All subclasses of RuntimeException
are unchecked exceptions, whereas all subclasses of Exception besides RuntimeException
are checked exceptions.
Java Errors vs Exceptions
According to the official documentation, an error “indicates serious problems that a reasonable application should not try to catch.” This refers to problems that the application can not recover from - they should be dealt with by modifying application architecture or by refactoring code.
Here is an example of a method that throws a error, which is not handled in code:
public static void print(String myString) {
print(myString);
}
In this example, the recursive method “print” calls itself over and over again until it reaches the maximum size of the Java thread stack, at which point it exits with a StackOverflowError
:
Exception in thread "main" java.lang.StackOverflowError
at StackOverflowErrorExample.print(StackOverflowErrorExample.java:6)
As seen above, the method throws the error during execution but does not handle it in code - the program simply exits when the error occurs since it is irrecoverable and requires a change in the code itself.
Exceptions, on the other hand, indicate “conditions that a reasonable application might want to catch.” These could include problems that can occur at compile-time (checked exceptions) or run-time (unchecked exceptions) and can happen rather frequently in most applications - especially during development. Checked exceptions should be handled in application code, whereas unchecked exceptions don’t need to be handled explicitly.
Checked vs Unchecked Exceptions
Checked Exceptions
Exceptions that can occur at compile-time are called checked exceptions since they need to be explicitly checked and handled in code. Classes that directly inherit Throwable - except RuntimeException
and Error - are checked exceptions e.g. IOException
, InterruptedException
etc.
Here is an example of a method that handles a checked exception:
public void writeToFile() {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("myFile.txt"))) {
bw.write("Test");
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
In this example, both statements within the try block (the instantiation of the BufferedWriter
object and writing to file using the object) can throw IOException
, which is a checked exception and therefore needs to be handled either by the method or its caller. In the example, IOException
is handled within the method and the exception stack trace is printed to the console.
Furthermore, the BufferedWriter
object is a resource, which should be closed when it is no longer needed and closing it can throw an IOException
as well. In such cases where closing resources themselves can throw exceptions, using a try-with-resources block is best practice since this takes care of the closing of resources automatically. The example shown earlier uses try-with-resources for exactly this reason.
While checked exceptions force explicit handling, this doesn't necessarily lead to better code. In fact, many modern Java frameworks and libraries prefer unchecked exceptions for their cleaner API design. The Spring framework, for instance, translates checked exceptions into unchecked ones to avoid cluttering method signatures.
Unchecked Exceptions
Unchecked exceptions can be thrown "at any time" (i.e. run-time). Therefore, methods don't have to explicitly catch or throw unchecked exceptions. Classes that inherit RuntimeException
are unchecked exceptions e.g. ArithmeticException
, NullPointerException
.
Here is an example of a method that throws an unchecked exception (NullPointerException) which is not handled in code:
public void writeToFile() {
try (BufferedWriter bw = null) {
bw.write("Test");
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
When the above method is called, a NullPointerException is thrown because the BufferedWriter object is null:
Exception in thread "main" java.lang.NullPointerException
at IOExceptionExample.writeToFile(IOExceptionExample.java:10)
at IOExceptionExample.main(IOExceptionExample.java:17)
As mentioned, since NullPointerException
is an unchecked exception, it did not need to be handled in code - only the checked exception (IOException) was handled.
Best Practices for Exception Handling
While understanding the exception hierarchy is important, how you use exceptions in your code matters even more. Consider these key practices:
- Be specific with exception types - catch the most specific exception possible rather than using catch-all blocks
- Don't catch exceptions you can't handle meaningfully
- Preserve the original exception when wrapping exceptions using the cause parameter
- Always use try-with-resources for files, connections, and other closeable resources to prevent memory leaks
- Help your team by documenting expected exceptions in Javadoc with @throws tags
- Implement proper error monitoring in production environments to track and analyze exceptions that occur in real-world usage
By applying these practices alongside a solid understanding of Java's exception hierarchy, you'll create more robust and maintainable applications.
Track, Analyze and Manage Errors With Rollbar
Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing errors easier than ever. Try it today!