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";
}
}
假设有一个 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";
}
}