Java 未处理的异常消失

Java unhandled exception disappears

假设有一个 class 使得:

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!");
    }
}
}

如果执行这段代码,会抛出空指针

container.sayHi();

container 实际上应该为空。在分配完成之前,当我们调用 getContainer() 时会抛出 ArrayIndexOutOfBoundException。但是,ArrayIndexOutOfBoundException 发生了什么?为什么我们在未处理的异常后进入 finally{}?

编辑:措辞不佳。问题是为什么我们 直接 进入 finally{}。 ArrayIndexOutOfBoundException

会发生什么

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

我们总是在块退出后转到 finally(成功,在异常处理程序之后,或在未处理的异常之后)。这正是 finally 的用途:一个放置代码的地方,无论如何都将是 运行。

However, what happens to the ArrayIndexOutOfBoundException?

如果您在异常处理程序或 finally 块中遇到第二个异常,那么将传播第二个异常并隐藏原始异常。

如果你想保留原来的异常,你可以通过Throwable#addSuppressed手动将它附加到新的异常(或者反过来,重新抛出原来的异常并附加新的被抑制的异常) .

Java 中有一个简单的规则:finally 总是 被调用。1

那么会发生什么:

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

这会抛出一个未被捕获的 ArrayIndexOutOfBoundException。在异常冒泡之前,调用 finally

container.sayHi();

container == null 所以 NullPointerException 被抛出,这掩盖了原来的异常。根据 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

...然后

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).

强调我的

1: 调用 System.exit 或其他一些罕见情况时除外。

在 finally 块中执行控制流操作时,您可能会得到另一个惊喜:

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

private static String badFunction() {
    try {
        throw new RuntimeException("Catch it!");
    } finally {
        return "Exception disappears";
    }
}