Reputation: 183
In normal try-catch-finally, like this,
try {
throw new ExceptionA();
} finally {
throw new ExceptionB();
}
ExceptionA is thrown before Exception B. ExceptionA will be suppressed.
But in try-with-resource, like this,
try ( // declare resource that throw ExceptionA in close method ) {
throw new ExceptionB();
}
ExceptionA is thrown after ExceptionB. ExceptionA will be suppressed.
Why do they have different orders of suppressing exceptions?
Upvotes: 2
Views: 1280
Reputation: 96394
When you use try and finally without try-with-resources, when something goes wrong in a try block, you get an exception thrown, then the finally is executed, and if an exception gets thrown during the finally block then the exception thrown by the finally masks the exception thrown by the try block. "Exception-masking" is where the JVM chooses that the exception to be thrown from the try-finally is the one from the finally block, not the original exception. This can be very bad because the exception thrown by the try block is the one with the information about what went wrong, the exception thrown by the finally is usually just noise. So in your example above with exception A, what happens is intuitive from the point of view of the implementer, but it isn't useful to the application developer; the valuable information about what actually went wrong is in A and you lose it, instead what is thrown is B and B's stacktrace is what you see when you read the log file.
For instance: my code makes a JDBC call that throws an exception with a line number telling me where the error happened and a SQLState that I can map back to a vendor code telling me what went wrong, but then when closing the statement or the connection there's a network glitch as the JDBC objects are telling the server what the server should clean up, and I get a broken-pipe exception. Unless you're very thorough with your exception handling it's easy to mask the useful exception with the broken-pipe one, the one you can't do anything about and don't care about.
The try-with-resources feature tries to make sure that informative exceptions don't get masked by incidental exceptions thrown on close, the exception thrown within the try block is what gets thrown and the one thrown by a close method on the way out is suppressed, meaning it gets added onto the exception from the try block (unless nothing was thrown in the try block, in which case the exception thrown on close is what gets thrown).
So this is a change, but it's a big improvement in reducing the chance of valuable exceptions getting masked inadvertently.
Upvotes: 6
Reputation: 81189
A weakness of the C++ exception-handling paradigm, which was largely inherited by Java and in turn by .NET, is that it cannot effectively handle situations where an exception occurs which requires cleanup (stack unwinding), and the process of unwinding the stack from the first exception triggers a second. In C++, the second exception will basically kill everything. The designers of Java and .NET apparently didn't like that behavior, but neither framework had any means of unwinding two exceptions simultaneously. The designers of Java decided that the least-of-evils behavior would be to have the system abandon the first exception (and any attempts to unwind the stack from it) when the second exception is thrown, but even if it's the "least of evils" it's still pretty evil. The implementers of .NET followed the Java pattern.
To really handle the situation cleanly would require providing cleanup code with information about whether it was being run in response to an exception and, if so, what it was. If an exceptional condition occurred in cleanup code, such information would make it possible for the cleanup code to ensure that if it fails, it will only replace the earlier exception if it's more important, and also ensure that evidence of the earlier exception is preserved in any event. Unfortunately, there's no standard convention for letting cleanup code know why it's being run, other than by icky duplication of code in catch
and try
blocks.
The "try with resources" construct assumes that the exception which occurred first is more likely to be important to the calling code, though it preserves evidence of exceptions which occur on cleanup. This isn't as good as allowing the cleanup code determine whether its exception is more or less important than the original, but it's better than having cleanup code either destroy all evidence of earlier exceptions, or having refrain from throwing exceptions altogether because it can't tell when it would be able to do so without stifling other exceptions.
Upvotes: 2
Reputation: 121750
This is because in a try-with-resources
statement, all resources are closed immediately after the try block, even if the try block throws an exception:
try (
// resource a which throws A when closed
) {
// exception B thrown from here
} // resource a closed HERE
which means that it is perfectly logical that B
is thrown first and A
second.
Therefore A
will be "suppressed by" (attached to) B
and not the reverse.
Of course, this stands true if you can open resource a
in the first place; if opening it throws an exception, B
will have no chance of being thrown at all...
Upvotes: 0