在Java中,执行如下代码:
public class AException extends Exception{
public String toString(){
return "A";
}
}
public class BException extends Exception{
public String toString(){
return "B";
}
}
public class Test {
public static void fun(){
try{
try{
throw new AException();
}finally{
throw new BException();
}
}catch(Exception e){
System.out.println(e);
}
}
public static void main(String[] args) {
fun();
}
}
会发现输出结果为:
B
即在Test的fun()方法中原本被抛出的AException并没有被捕获。这是由于Java虚拟机的机制造成的一点缺陷。
解释:由于在finally中的代码一般是关闭资源的代码(关闭文件、网络链接等),所以当程序执行至try/catch代码块的“边界处”时,便会转入finally代码块,而throw语句在字节码层面并非原语操作,所以当上面程序在执行到
throw new AException();
时,jvm会将要抛出的异常的对象的引用存放到一个局部变量里,并将该变量存到方法栈的栈顶等待弹出,此时程序计数器指针指向finally内的代码,遇到下一个要抛出的异常,该异常则顶替AException的对象引用所在位置,所以程序只会输出:B
上述异常丢失现象在Java1.8版本中依然存在。
同理,在finally里也不要处理返回值。(当返回值在finally块外返回时,由于throw并非原子语句,所以会用中间变量存储中间值,导致finally内处理的返回值并不能体现在返回的实际值上)