Java provides a robust and efficient exception handling framework used in all sorts of applications irrespective of their size. Moreover, it allows us to create custom exceptions and handlers to better drive the application flow.
If something goes wrong during the execution of a method, the method creates an instance of an
Exception class that contains all the relevant details in the form of the stack trace and hands it over to the runtime, i.e., JVM. This process is known as throwing an exception.
The stack trace contains an ordered list of the method call hierarchy that was followed originally to reach the point where that exception has occurred.
Using the details, the JVM will try to identify a place in the call hierarchy that can handle the
Exception instance, i.e., an intermediate method in the call hierarchy capable of handling either the specific exception or any of its parent types. For example: a method capable of handling
IOException will take all the
FileNotFoundException exceptions as well.
Consider the following example:
In the absence of an exception handler in the calculateArea method, the
IllegalArgumentException from method validate will be forwarded to logInputAndCalculateArea method - incase an invalid input is passed.
In case no exception handler is found in the complete call hierarchy, the exception instance is forwarded to the default exception handler, and the program terminates abruptly.
Now, one thing is clear from the details mentioned above: a java method will only allow a single exception to be thrown at any given point. But there could be multiple exceptions from a single method; how do we ensure that correct details are propagated to the client code?
Consider another example:
In case of an invalid path, the try block will throw a
FileNotFoundException and the finally block will throw a
NullPointerException. In such cases, it is the exception thrown from the finally block that will reach the calling code. The issue in such cases is that the client cannot recover the original exception thrown from the try block that caused the finally block to fail.
Java 1.7 introduced the concept of suppressed exceptions. A suppressed exception is an exception that is ignored to let another exception be thrown from the method. A new method addSuppressed was added to the
Throwable class to append such exceptions to the current exception being thrown.
As shown in the code mentioned above, we can append the optionalException to the
NullPointerException instance. The client can then retrieve the original exception based on the requirement and can handle it accordingly. Here is a quick spock test to demonstrate this:
In addition to this, suppressed exceptions play an important role while working with try-with-resources blocks.
The close method of an
AutoClosable class is treated as an implicit finally block when working with try-with-resources block. In case an exception is thrown from the try block, and another exception is thrown from the close method, it is the exception thrown from the try block that will reach the client, and the one thrown from the close method, is added as a suppressed exception by the runtime itself:
And here is another test case to confirm:
That is all for this post. If you want to share any feedback, please drop me an email, or contact me on any of the social platforms. I’ll try to respond at the earliest. Also, please consider subscribing for regular updates.
You can check the code in the github repo.
jvm aware newsletter
join the newsletter to receive the latest updates in your inbox.