使用 return 的 Try-finally 块不会引发异常
Try-finally block with return doesn't rise exception
在这段代码中,main 方法的 Catch 没有捕获运行时异常。 finally块执行完后,本该进入main的异常块,但是没有。
class FinallyDemo {
static int m1(){
try{
System.out.println("Inside m1");
throw new RuntimeException("hi");
}
finally {
System.out.println("m1 finally");
return 5;
}
}
public static void main(String[] args) {
try{
System.out.println(m1());
}catch (Exception e){
System.out.println("main caught: "+ e);
}
}
}
输出:
Inside m1
m1 finally
5
(如评论中所述,编写的代码甚至无法编译,但可以在不改变问题核心的情况下修复。)
it should've gone to the exception block of main, but it doesn't.
不,它的行为完全符合规范 section 14.20.2。那里有很多路径,但这里应用的路径是:
- ...
- If execution of the
try
block completes abruptly because of a throw of a value V
- ...
- If the run-time type of V is not assignment compatible with a catchable exception class of any
catch
clause of the try statement, then the finally
block is executed
- ...
- 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).
根据 section 14.1,return
语句算作“突然完成”。所以整个 try/finally 语句由于“return 有一个值”的原因突然完成,就好像没有抛出异常一样。
如果您希望异常传播到 try
/finally
语句之外,请不要在 finally
块中使用 return
语句。
这是 class 的字节码。
static int m1();
descriptor: ()I
flags: ACC_STATIC
Code:
stack=3, locals=1, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Inside m1
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: new #5 // class java/lang/RuntimeException
11: dup
12: ldc #6 // String hi
14: invokespecial #7 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
17: athrow
18: astore_0
19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #8 // String m1 finally
24: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: iconst_5
28: ireturn
Exception table:
from to target type
0 19 18 any
查看此程序后。
public class FinallyDemo {
static void m1(){
try{
System.out.println("Inside m1");
throw new RuntimeException("hi");
}
finally {
System.out.println("m1 finally");
}
}
public static void main(String[] args) {
try{
m1();
}catch (Exception e){
System.out.println("main caught: "+ e);
}
}
}
字节码。
static void m1();
descriptor: ()V
flags: ACC_STATIC
Code:
stack=3, locals=1, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Inside m1
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: new #5 // class java/lang/RuntimeException
11: dup
12: ldc #6 // String hi
14: invokespecial #7 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
17: athrow
18: astore_0
19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #8 // String m1 finally
24: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: aload_0
28: athrow
Exception table:
from to target type
0 19 18 any
在我看来,athrow本质上是一个跳转语句。
很明显,在finally中加入了一条return语句,编译时省略了athrow语句,因为确定不可达
在这段代码中,main 方法的 Catch 没有捕获运行时异常。 finally块执行完后,本该进入main的异常块,但是没有。
class FinallyDemo {
static int m1(){
try{
System.out.println("Inside m1");
throw new RuntimeException("hi");
}
finally {
System.out.println("m1 finally");
return 5;
}
}
public static void main(String[] args) {
try{
System.out.println(m1());
}catch (Exception e){
System.out.println("main caught: "+ e);
}
}
}
输出:
Inside m1
m1 finally
5
(如评论中所述,编写的代码甚至无法编译,但可以在不改变问题核心的情况下修复。)
it should've gone to the exception block of main, but it doesn't.
不,它的行为完全符合规范 section 14.20.2。那里有很多路径,但这里应用的路径是:
- ...
- If execution of the
try
block completes abruptly because of a throw of a value V
- ...
- If the run-time type of V is not assignment compatible with a catchable exception class of any
catch
clause of the try statement, then thefinally
block is executed
- ...
- If the
finally
block completes abruptly for reason S, then thetry
statement completes abruptly for reason S (and the throw of value V is discarded and forgotten).
根据 section 14.1,return
语句算作“突然完成”。所以整个 try/finally 语句由于“return 有一个值”的原因突然完成,就好像没有抛出异常一样。
如果您希望异常传播到 try
/finally
语句之外,请不要在 finally
块中使用 return
语句。
这是 class 的字节码。
static int m1();
descriptor: ()I
flags: ACC_STATIC
Code:
stack=3, locals=1, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Inside m1
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: new #5 // class java/lang/RuntimeException
11: dup
12: ldc #6 // String hi
14: invokespecial #7 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
17: athrow
18: astore_0
19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #8 // String m1 finally
24: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: iconst_5
28: ireturn
Exception table:
from to target type
0 19 18 any
查看此程序后。
public class FinallyDemo {
static void m1(){
try{
System.out.println("Inside m1");
throw new RuntimeException("hi");
}
finally {
System.out.println("m1 finally");
}
}
public static void main(String[] args) {
try{
m1();
}catch (Exception e){
System.out.println("main caught: "+ e);
}
}
}
字节码。
static void m1();
descriptor: ()V
flags: ACC_STATIC
Code:
stack=3, locals=1, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Inside m1
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: new #5 // class java/lang/RuntimeException
11: dup
12: ldc #6 // String hi
14: invokespecial #7 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
17: athrow
18: astore_0
19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #8 // String m1 finally
24: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: aload_0
28: athrow
Exception table:
from to target type
0 19 18 any
在我看来,athrow本质上是一个跳转语句。 很明显,在finally中加入了一条return语句,编译时省略了athrow语句,因为确定不可达