1

Suppose there is a class such that:

public class Magic {

public static void main(String[] args){
    boolean[] val = new boolean[0];
    paradox(val);
}

private static boolean paradox(boolean[] arg) {
    Container container = null;
    try {
        container = Container.getContainer(arg[0]);
        return container.value;
    } catch (UnsupportedOperationException e) {
        e.printStackTrace();
        return false;
    } finally {
        try {
            container.sayHi();
        } catch (UnsupportedOperationException e) {
            e.printStackTrace();
        }
    }
}

private static class Container{
    private boolean value;

    private Container(boolean value){
        this.value = value;
    }

    private static Container getContainer(boolean value) throws UnsupportedOperationException{
        return new Container(value);
    }

    private void sayHi(){
        System.out.println("Hi!");
    }
}
}

If this code is executed, there is a null pointer thrown on line with

container.sayHi();

container should, in fact, be null. Before the assignment can complete there is an ArrayIndexOutOfBoundException thrown when we call getContainer(). However, what happens to the ArrayIndexOutOfBoundException? Why do we go into finally{} after an unhandled exception?

edit: poor phrasing. question is why we go directly into finally{}. And what happens to ArrayIndexOutOfBoundException

aiguy
  • 671
  • 5
  • 20

3 Answers3

6

Why do we go into finally{} after an unhandled exception?

We always go to finally after a block exits (successfully, after an exception handler, or after an unhandled exception). That's exactly what finally is for: a place to put code that will be run no matter what.

However, what happens to the ArrayIndexOutOfBoundException?

If you encounter a second exception in an exception handler or a finally block, then that second exception will be propagated and the original exception will be hidden.

If you want to preserve the original exception, you can manually attach it to the new exception via Throwable#addSuppressed (or the other way around, re-throw the original exception and attach the new one as suppressed).

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 1
    the problem is how to access the original exception in `finally` โ€“ ZhongYu Feb 11 '16 at 00:08
  • That's true. Here's how it is implemented for the auto-generated finally blocks of the `try-with-resources` construct: http://stackoverflow.com/questions/24181489/why-does-java-not-support-retrieval-of-exceptions-from-try-catch-lost-when-an-ex?rq=1 First rule of business is probably to make sure `finally` does not raise any more exceptions in the first place. โ€“ Thilo Feb 11 '16 at 00:17
3

There is a simple rule in Java: finally is always called.1

So what happens is this:

container = Container.getContainer(arg[0]);

This throws an ArrayIndexOutOfBoundException, which is uncaught. Before the exception bubbles, finally is called.

container.sayHi();

container == null so a NullPointerException is thrown, this shadows the original exception. As per the JLS ยง14.20.2

If execution of the try block completes abruptly because of a throw of a value V, then there is a choice

... then

If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and the throw of value V is discarded and forgotten).

emphasis mine

1: except when System.exit is called or some other rare cirumstances.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
2

Another surprise you may get when doing control flow operations inside finally block :

public static void main(String[] args) {
    System.out.println(badFunction());
}

private static String badFunction() {
    try {
        throw new RuntimeException("Catch it!");
    } finally {
        return "Exception disappears";
    }
}
Bax
  • 4,260
  • 5
  • 43
  • 65
  • 1
    yes, `finally{}` should always complete uneventfully. (except for throwing Error which can never be prevented) โ€“ ZhongYu Feb 11 '16 at 00:13