A comment (by user soc) on an answer to a question about tail call optimisation mentioned that Java 7 has a new feature called \"suppressed exceptions\", because of \"the ad
I believe the commenter is referring to is an exception which is semi-ignored when it's thrown within the implicit finally
block of a try-with-resources block, in the context of an existing exception being thrown from the try
block:
An exception can be thrown from the block of code associated with the try-with-resources statement. In the example writeToFileZipFileContents, an exception can be thrown from the try block, and up to two exceptions can be thrown from the try-with-resources statement when it tries to close the ZipFile and BufferedWriter objects. If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed, and the exception thrown by the block is the one that is thrown by the writeToFileZipFileContents method. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try block.
(That's quoting a section called "Suppressed Exceptions" from the linked page.)
Concedering the code below:
public class MultipleExceptionsExample {
static class IOManip implements Closeable{
@Override
public void close() {
throw new RuntimeException("from IOManip.close");
}
}
public static void main(String[] args) {
try(IOManip ioManip = new IOManip()){
throw new RuntimeException("from try!");
}catch(Exception e){
throw new RuntimeException("from catch!");
}finally{
throw new RuntimeException("from finally!");
}
}
}
With all lines you will get: java.lang.RuntimeException: from finally!
Removing finally
block you will get: java.lang.RuntimeException: from catch!
Removing catch
block you will get:
Exception in thread "main" java.lang.RuntimeException: from try!
Suppressed: java.lang.RuntimeException: from IOManip.close
Before Java7; There are exceptions thrown in the code but were ignored somehow.
e.g.)
public class SuppressedExceptions {
public static void main(String[] args) throws Exception {
try {
callTryFinallyBlock();
} catch (Exception e) {
e.printStackTrace(); **//Only Finally Exception is Caught**
}
}
private static void callTryFinallyBlock() throws Exception {
try
{
throw new TryException(); **//This is lost**
}
finally
{
FinallyException fEx = new FinallyException();
throw fEx;
}
}
}
class TryException extends Exception {
}
class FinallyException extends Exception {
}
A new constructor and two new methods were added to the Throwable class in JDK 7. These are as below:
Throwable.getSupressed(); // Returns Throwable[]
Throwable.addSupressed(aThrowable);
with this new approach, we can handle those suppressed exception as well.
public class SuppressedExceptions {
public static void main(String[] args) throws Exception {
try {
callTryFinallyBlock();
} catch (Exception e) {
e.printStackTrace();
for(Throwable t: e.getSuppressed())
{
t.printStackTrace();
}
}
}
private static void callTryFinallyBlock() throws Exception {
Throwable t = null;
try
{
throw new TryException();
}
catch (Exception e) {
t = e;
}
finally
{
FinallyException fEx = new FinallyException();
if(t != null)fEx.addSuppressed(t);
throw fEx;
}
}
}
class TryException extends Exception {
}
class FinallyException extends Exception {
}
In Java7 try-with-resources; the exception at AutoCloseable::close() is added as suppressed exception by default along with try exception.
Also aware that this is different from chained exceptions (were introduced with JDK 1.4 and were intended to make it possible to easily track causal relationships between exceptions.)
To clarify the quote in Jon's answer, only one exception can be thrown by a method (per execution) but it is possible, in the case of a try-with-resources
, for multiple exceptions to be thrown. For instance one might be thrown in the block and another might be thrown from the implicit finally
provided by the try-with-resources
.
The compiler has to determine which of these to "really" throw. It chooses to throw the exception raised in the explicit code (the code in the try
block) rather than the one thrown by the implicit code (the finally
block). Therefore the exception(s) thrown in the implicit block are suppressed (ignored). This only occurs in the case of multiple exceptions.
I think this has to do with the "chained exception facility". It will affect how an exception is handled by this facility as the stack trace evolves. Over time exceptions that are part of a group of chained exception can be suppressed. Look at the Throwable documentation for more details.
ARM - Automatic Resource Management(Introduced since Java 7)
Take a very simple example
static String readFirstLineFromFileWithFinallyBlock(String path)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
Now if readLine()
function throws Exception and then even close()
function [in finally block] throws exception then the later is given more priority and is thrown back to the calling function. In this case the Exception thrown by the readLine() method is ignored/suppressed
. You can chain the causing exception in your exception and rethrow your exception from finally block.
Since java 7
functionality has been provided to retrieve suppressed Exceptions. You can call public final java.lang.Throwable[] getSuppressed()
function on the catched throwable object to view the suppressed Exceptions.
For Eg.
static String readFirstLineFromFileWithFinallyBlock(String path)
throws Exception {
try (BufferedReader br = new BufferedReader(new FileReader(path));) {
return br.readLine();
}
}
Now if br.readLine();
line throws Exception1
and then lets say Exception2
is thrown while closing the resource [Imagine this happening in an implicit finally block that try-with-resource statement creates] then Exception1 suppresses Exception2.
Few points to note here -
I have compiled most of the possible scenarios with code snippets and output in following post.
Suppressed exceptions in java 7
Hope that helps.