先来看段代码:
public class ExceptionHandler {
public ExceptionHandler() {
}
boolean testEx() throws Exception {
boolean ret = true;
try {
ret = testEx1();
} catch (Exception e) {
System.out.println("testEx, catch exception");
ret = false;
throw e;
} finally {
System.out.println("testEx, finally; return value=" + ret);
return ret;
}
}
boolean testEx1() throws Exception {
boolean ret = true;
try {
ret = testEx2();
if (!ret) {
return false;
}
System.out.println("testEx1, at the end of try");
return ret;
} catch (Exception e) {
System.out.println("testEx1, catch exception");
ret = false;
throw e;
} finally {
System.out.println("testEx1, finally; return value=" + ret);
return ret;
}
}
boolean testEx2() throws Exception {
boolean ret = true;
try {
int b = 12;
int c;
for (int i = 2; i >= -2; i--) {
c = b / i;
System.out.println("i=" + i);
}
return true;
} catch (Exception e) {
System.out.println("testEx2, catch exception");
ret = false;
throw e;
} finally {
System.out.println("testEx2, finally; return value=" + ret);
return ret;
}
}
public static void main(String[] args) {
ExceptionHandler testException1 = new ExceptionHandler();
try {
testException1.testEx();
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果如下:
i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, finally; return value=false
testEx, finally; return value=false
我相信这个结果一定出乎很多人的意外,好像testEx2中最后抛出的异常丢失了,这就是我们通常说的异常丢失。那么,异常丢失是怎样造成的呢?我们还是祭出我们的手术刀javap来看看。
先看看testEx2函数的字节码
boolean testEx2() throws java.lang.Exception;
Code:
0: iconst_1
1: istore_1
2: bipush 12
4: istore_2
5: iconst_2
6: istore 4
8: goto 42
11: iload_2
12: iload 4
14: idiv
15: istore_3
16: getstatic #22 // Field java/lang/System.out:Ljava/io/PrintStream;
19: new #36 // class java/lang/StringBuilder
22: dup
23: ldc #66 // String i=
25: invokespecial #40 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
28: iload 4
30: invokevirtual #68 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
33: invokevirtual #46 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
36: invokevirtual #30 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
39: iinc 4, -1
42: iload 4
44: bipush -2
46: if_icmpge 11
49: goto 66
52: astore_2
53: getstatic #22 // Field java/lang/System.out:Ljava/io/PrintStream;
56: ldc #71 // String testEx2, catch exception
58: invokevirtual #30 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
61: iconst_0
62: istore_1
63: aload_2
64: athrow
65: pop
66: getstatic #22 // Field java/lang/System.out:Ljava/io/PrintStream;
69: new #36 // class java/lang/StringBuilder
72: dup
73: ldc #73 // String testEx2, finally; return value=
75: invokespecial #40 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
78: iload_1
79: invokevirtual #42 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
82: invokevirtual #46 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
85: invokevirtual #30 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
88: iload_1
89: ireturn
Exception table:
from to target type
2 52 52 Class java/lang/Exception
2 65 65 any
可以看到,finally语句块对应的字节码一开始就有pop操作,而catch语句开在结束的时候通过athrow指令将栈顶的异常抛出,这一对正反操作正好把栈上的异常对象的引用给丢掉了,从而造成了异常丢失现象。testEx1和testEx函数的字节码这里就不再赘述,大家自行观察。
注:由于目前还未找到观察操作数栈的工具,无法对上述内容进行验证,作者不对内容的正确性负责。另外,如果哪位朋友知道观察操作数栈的方法或工具,请不吝告知,谢谢!MAIL:zxh65 at sohu.com