Handling Java exceptions can be tricky.
Which Java exceptions should you catch, and which exceptions should you rethrow? And which exceptions can you ignore completely? This is a difficult skill to develop.
These Java Exception handling best practices provide software developers guidance when they develop new applications.
Top 10 Java Exception handling best practices
Here are 10 Java exception handling best practices software developers should follow:
- Be careful what you log.
- Don’t bury thrown exceptions.
- Use a top-level exception handler.
- Don’t close resources manually.
- Throw early and handle exceptions late.
- Don’t log and rethrow Java exceptions.
- Check for suppressed exceptions.
- Explicitly define exception in the throws clause.
- Catch the most explicit exception first.
- Use modern exception handling semantics.
Be careful what you log
Software developers must always be mindful of the customer’s security and privacy rights.
One can move data logged by a Java application to various low-cost storage system, to be viewed by developers and administrators as they attempt to solve a bug.
However, you must ensure no protected data is written in the log files. Otherwise, your company will be out of compliance, and you might be out of a job.
Don’t bury thrown exceptions
Don’t catch an exception and then do nothing with it. That’s known as burying an exception, and it is definitely not a Java exception handling best practice.
At the very least, log the name of the exception and the message associated with it. That way you or others can extract information about an exceptional situation from the logs.
Buried exceptions makes troubleshooting Java applications extremely hard.
Use a top-level exception handler
Even well-tested applications can encounter an unexpected unchecked exception.
Handle unexpected failures at an appropriate application boundary, such as an HTTP exception mapper, request filter, framework error handler or top-level task boundary. Log the failure once, return an appropriate error response when applicable, and let the current operation terminate cleanly. A top-level handler should not blindly allow an application to continue after an unknown failure.
Don’t close resources manually
Another important Java exception handling best practice is to allow the JVM to call the close() method of closeable resources. Don’t close resources yourself.
This is easily accomplished if you initialize resources within a try-with-resources block. The following example shows the try-with-resources semantics in action:
final class Door implements AutoCloseable {
void swing() {
IO.println("The door swings open.");
}
@Override
public void close() {
IO.println("The door closes automatically.");
}
}
void main() {
try (var door = new Door()) {
door.swing();
}
}The try-with-resources statement invokes close() automatically when control leaves the block, including when an exception is thrown. This greatly reduces the risk of resource leaks.
Throw early and handle exceptions late
As soon as an exception condition happens in your code, throw an Exception. Don’t wait for any additional lines of code to run before you terminate execution of the method you are in.
The function to catch exceptions should go toward the end of a method. This puts fewer catch blocks in your methods, and makes your code much easier to read and maintain.
Don’t log and rethrow
When an Exception occurs you should either:
- log the Exception and carry on with your application; or
- rethrow the Exception and let another method log the details
Never do both. You should never log and then rethrow the same exception, as is done in the following example:
/* Avoid logging an exception here if a higher layer will log it again. */
try {
processRequest();
} catch (IOException ex) {
throw new IllegalStateException("Unable to process the request", ex);
}Doing so causes code duplication, and litters the log files with duplicate entries which makes it much more difficult to troubleshoot code.
Check for suppressed exceptions
The suppressed exception is a relatively new language feature that not all developers are aware of.
Basically, the introduction of the try-with-resources function, made it possible to throw two exceptions at the same time. You can easily check this situation simply by querying for the suppressed exception’s existence, as is done in the example below:
try (var resource = openResource()) {
resource.process();
} catch (Exception ex) {
IO.println("Primary exception: " + ex);
for (var suppressed : ex.getSuppressed()) {
IO.println("Suppressed exception: " + suppressed);
}
}The only way to know if this scenario has occurred is to check if the target Exception also contains a suppressed exception.
Explicitly define exceptions in the throws clause
Lazy developers use the generic Exception class in the throws clause of a method. Doing so is not a Java exception handling best practice.
Instead, always explicitly state the exact set of exception a given method might throw. This allows other developers to know the various error handling routines they can employ if a given method fails to execute properly.
Catch the most specific exception first
This is more of a compiler requirement than a Java exception handling best practice, but a developer should always catch the most specific exception first, and the least specific one last.
If this rule is not followed, the JVM will generate a compile time error with a fairly cryptic error message that is difficult to understand.
Make your software development life easier, and always catch the most specific exception in your code first.
Use modern exception handling techniques
Is Java verbose? It is if you stick with the 20-year-old exception handling techniques.
Java has added many features around error and exception handling that simplify development and greatly reduce the verbosity of Java code.
Use multi-catch when several exception types have the same recovery logic, use try-with-resources for AutoCloseable resources, preserve exception causes when translating exceptions, and choose checked or unchecked exceptions according to whether callers can reasonably recover from the failure.
Follow these 10 Java exception handling best practices, and you’ll find yourself learning to love dealing with exceptions in Java.
Cameron McKenzie is an AWS Certified AI Practitioner, Machine Learning Engineer, Solutions Architect and author of many popular books in the software development and Cloud Computing space. His growing YouTube channel training devs in Java, Spring, AI and ML has well over 30,000 subscribers.