0

Lets say I have a class that has two OutputStream members:

public class A {
    OutputStream os1 = ...
    OutputStream os2 = ...

    ...
}

Now, in this class, I have a cleanup() method which must at least attempt to close both streams:

public void cleanup() throws IOException {
    try {
        os1.close();
    } finally {
        os2.close();
    }
}

Now, I'm confident that the above code will try to close both streams, but what is interesting is what happens when both calls to close() throw an IOException. I want to say that the exception from os2 will propogate upward, but perhaps the behavior is undefined. I've done some googling but I'm not finding anything that answers this question well.

Ben
  • 7,692
  • 15
  • 49
  • 64
  • A cleanup method like that is a code smell. You should [use `try`-with-resources instead](https://stackoverflow.com/a/56112599/545127). – Raedwald May 13 '19 at 12:59

3 Answers3

2

If the finally throws an exception, then the method terminates with that exception. It's very well defined behavior. Likewise, if a finally returns, then the method returns with the finally's return value, regardless of any previous return from the associated try block.

Ernest Friedman-Hill
  • 80,601
  • 10
  • 150
  • 186
2

The JLS §14.20.2 describes the behaviour. The exception, that is thrown by the method is the exception caused by os2.close().

fabian
  • 80,457
  • 12
  • 86
  • 114
0

Yes, the second exception is propagated upwards, and the first exception is lost.

But if you're using Java 7 or better, it's far better to use the "try with resources" syntax. The code is much cleaner, there's a lot less to write, and it'll do a far better job than you can of making sure the resources get closed when they should, no matter what. For example, no matter how smart and fancy you make cleanup(), if it doesn't get called then you're out of luck. What if the context that's supposed to call it itself throws an exception? Don't do it that way!

Alvin Thompson
  • 5,388
  • 3
  • 26
  • 39
  • Not all resources are initialized and cleaned under the same scope. As so, you cannot use the "try with resources" syntax if you initialize a stream in a class constructor and close it in a method. – E_net4 May 24 '14 at 00:32
  • @E_net4 - If you show me code where a resource is not initialized and cleaned up in the same scope, I'll show you code that could be written better. – Alvin Thompson May 24 '14 at 00:41
  • Maybe the term "scope" became confusing here. But no example code is needed, just read and consider my previous comment. – E_net4 May 24 '14 at 09:05
  • @E_net4 - No confusion. I respectfully disagree. And are you honestly trying to argue that the poster shouldn't at least consider rewriting to take advantage of the "try with resources" syntax? – Alvin Thompson May 24 '14 at 11:36
  • Well, my point is that a try-with resources statement cannot start in one method and end in another. It lies in the same method scope, being initialized and closed in each method invocation. In OP's example, we're using resources that are meant to last for no longer than the object's life time. We cannot guarantee that. We can however, make them support try-with. http://pastebin.com/yeYZ74Vj – E_net4 May 24 '14 at 11:51
  • @E_net4 - First of all, no one said that a try-with resources statement can start in one method and end in another. Second, I disagree with your original statement that "you cannot use the 'try with resources' syntax if you initialize a stream in a class constructor and close it in a method" because it's clearly not true. Even the code you just supplied proves it's not true. `try (AnimalPrinter ap = new AnimalPrinter("foo")) { ... }` – Alvin Thompson May 24 '14 at 12:13
  • We're having a clear misunderstanding for too long, so allow me to make a final comment. Indeed, you can do `try (new AnimalPrinter("foo")) { ... }` because I made it `Closeable`. That is expected. But note how I couldn't avoid writing `os.close()` in the implementation of `AnimalPrinter`. "you cannot use the 'try with resources' syntax if you initialize a stream in a class constructor and close it in a method", because that would be extending a try statement through multiple method implementations, which doesn't make sense. I hope all is clear now, this doesn't even help OP. – E_net4 May 24 '14 at 12:20
  • @E_net4 - So your whole point was to say, "If you don't write the code correctly, it won't work?" Well, that's patently obvious, but thanks for the input. – Alvin Thompson May 24 '14 at 12:27