上篇文章从执行结果角度来看Java中try、catch、finally在各个场景下执行顺序,现在从字节码角度来再来看一遍:
1. try{return;}catch{}finally{} return;
1.1 源代码
public static int returnSttat(int i) {
System.out.println("return block" + i);
return i;
}
public static int test01() {
int i = 0;
try {
i = 1;
return returnSttat(i);
}catch (Exception e) {
} finally {
System.out.println("finally block01");
}
return 0;
}
1.2 字节码
public static int test01();
Code:
0: iconst_0
1: istore_0
2: iconst_1
3: istore_0
4: iload_0
5: invokestatic #46 // Method returnSttat:(I)I
8: istore_3
9: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
12: ldc #48 // String finally block01
14: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
17: iload_3
18: ireturn
19: astore_1
20: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
23: ldc #48 // String finally block01
25: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: goto 42
31: astore_2
32: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
35: ldc #48 // String finally block01
37: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: aload_2
41: athrow
42: iconst_0
43: ireturn
Exception table:
from to target type
2 9 19 Class java/lang/Exception
2 9 31 any
19 20 31 any
1.3 执行顺序分析
我们看第8~18行,我们可以从字节码命令看出在执行完try之后会将#3先存储起来,然后执行finally部分;最后将#3取出来返回给上层调用者.
2. try{}catch{}finally{} return 有异常场景;
2.1 源代码
public static int returnSttat(int i) {
System.out.println("return block" + i);
return i;
}
public static int test01() {
int i = 1;
int divid = 0;
try {
int n = i / divid;
} catch (Exception e) {
System.out.println("catch block");
return returnSttat(i);
} finally {
System.out.println("finally block");
}
return 0;
}
2.1 字节码
public static int test01();
Code:
0: iconst_1
1: istore_0
2: iconst_0
3: istore_1
4: iload_0
5: iload_1
6: idiv
7: istore_2
8: goto 48
11: astore_2
12: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #46 // String catch block
17: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: iload_0
21: invokestatic #48 // Method returnSttat:(I)I
24: istore 4
26: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
29: ldc #50 // String finally block
31: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
34: iload 4
36: ireturn
37: astore_3
38: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
41: ldc #50 // String finally block
43: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
46: aload_3
47: athrow
48: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
51: ldc #50 // String finally block
53: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
56: iconst_0
57: ireturn
Exception table:
from to target type
4 8 11 Class java/lang/Exception
4 26 37 any
}
2.3 执行顺序分析
我们直接从第6行看idiv指令抛出异常,1) 根据Exception Table取[4, 8, 11, Class java/lang/Exception]指令跳转到11开始执行; 2) 从11~21行均为catch块代码执行; 3) 行24将catch中return值记录在4中; 4)执行finally部分;5)取出4的值然后返回给调用方.
3. try{return;} catch{} finally{return;};
3.1 源代码
public static int returnSttat(int i) {
System.out.println("return block" + i);
return i;
}
public static int test01() {
int i = 1;
try {
System.out.println("try block");
return returnSttat(i);
} catch (Exception e) {
System.out.println("catch block");
}finally {
System.out.println("finally block");
return returnSttat(0);
}
}
3.2 字节码
public static int test01();
Code:
0: iconst_1
1: istore_0
2: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
5: ldc #46 // String try block
7: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: iload_0
11: invokestatic #48 // Method returnSttat:(I)I
14: pop
15: goto 31
18: astore_1
19: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #50 // String catch block
24: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: goto 31
30: pop
31: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
34: ldc #52 // String finally block
36: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
39: iconst_0
40: invokestatic #48 // Method returnSttat:(I)I
43: ireturn
Exception table:
from to target type
2 18 18 Class java/lang/Exception
2 27 30 any
}
3.3 执行顺序分析
我们可以看到1~14行为执行try块命令;1)15行通过goto语句跳转到31行(finally块); 2) 执行finally块,我们重点看40,43行,finally中执行了方法后就调用了ireturn指令,我们可以以此看出返回给调用方的是finally中return的语句的值(快速返回)
4. try{return;} catch{} finally{return;}有异常
4.1 源代码
public static int returnSttat(int i) {
System.out.println("return block" + i);
return i;
}
public static int test01() {
int i = 1;
int j = 0;
try {
System.out.println("try block");
int k = i / j;
return returnSttat(i);
} catch (Exception e) {
System.out.println("catch block");
return returnSttat(-1);
}finally {
System.out.println("finally block");
return returnSttat(0);
}
}
4.2 字节码
public static int test01();
Code:
0: iconst_1
1: istore_0
2: iconst_0
3: istore_1
4: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #46 // String try block
9: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: iload_0
13: iload_1
14: idiv
15: istore_2
16: iload_0
17: invokestatic #48 // Method returnSttat:(I)I
20: pop
21: goto 42
24: astore_2
25: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
28: ldc #50 // String catch block
30: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
33: iconst_m1
34: invokestatic #48 // Method returnSttat:(I)I
37: pop
38: goto 42
41: pop
42: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
45: ldc #52 // String finally block
47: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
50: iconst_0
51: invokestatic #48 // Method returnSttat:(I)I
54: ireturn
Exception table:
from to target type
4 24 24 Class java/lang/Exception
4 41 41 any
}
4.3 执行顺序分析
我们从第14行看执行idiv出现异常; 1) 根据Exception Table可以确定跳转到24行继续执行;2) 24~37行执行catch快部分; 3) 38行跳转到finally部分执行,我们重点看51,54行在finally中执行了静态方法后就调用了ireturn,实际上这调用返回的是finally中return表达式的结果
二、总结
从执行现象上看是可以得到上一篇文章对于执行顺序的结论,然而我只是深深的记住了这个结果,这从字节码的角度再来看一遍,对于try、catch、finally的执行顺序就一目了然了.