Firstly, sorry for asking a question on what must be a well-worn topic. I’ve come across loads of questions with answers suggesting to swallow (catch and ignore/log) potential exception from methods that close resources in finally blocks. It seems to be a generally accepted pattern. But I haven’t yet seen anybody explain why. Here’s just one example: Try-catch-finally and then again a try catch.
I understand that any exception thrown from a finally block will “mask” any exception thrown in the corresponding try block, but I don’t see why this is a bad thing. For example:
Resource r = new Resource();
try {
r.use();
other();
} finally {
r.close();
}
My current understanding is:
- If only
closethrows an exception, we definitely don’t want to swallow it. - If both
useandclosethrow an exception, it’s probably for the same underlying reason, and it doesn’t matter which exception gets propagated up (they both would contain equally useful information?). - If both
otherandclosethrow an exception, there are two unrelated problems. Arguably the one that happened first should propagate, but there isn’t any reason to suggest that the first exception caused the second (is there?), so either will do.
What am I wrong about?
That’s right!
Not really. The exception in
trycould have beenNullPointerException,OutOfMemoryErroror actual I/O exception describing the nature of the problem. Even if it’s an I/O exception, the error could have left the stream in inconsistent state (or even closed it prematurely). Callingclose()might yield completely different error, like “stream already closed“, “internal error” or whatever.One error in the system tends to cascade dozens of others, sometimes looking very unrelated. Always look for the first exception for the root cause. The rest is just garbage. A good example is an initialization that failed and left an object in inconsistent state. Later you see
NullPointerExceptions everywhere, but the real problem occurred way earlier.Actually, both exceptions are imports, but the first one is probably more relevant. Solution? Use
AutoCloseableresources in Java 7 and try-with-resources idiom. If exception occurs while cleaning up, it will be attached to original exception as suppressed:and then:
The exception will look something like this: