Java 中经常会用到异常处理。可是你真的了解try...catch...finally语句的执行过程吗?先来看看这段代码:
public class TestException {
public static void main(String[] args) {
TestException te = new TestException();
int a = te.test();
System.out.println("call block. a: " + a);
}
public int test()
{
int a = 1;
try
{ //【1】
a = a + 1; //a:2
System.out.println("try block. a: " + a);
//this will cause an exception
int b = a / 0; //【2】
return a; //【3】
}
catch (Exception e)
{//【4】
a = a + 2; //a:4
System.out.println("catch block. a: " + a);//【5】
return a; //【6】
}
finally
{//【7】
a = a + 3; //a:7
System.out.println("finally block. a: " + a);//【8】
//return a; //【9】
}
//return a;
}//eof:test()
}
问题来了: main函数中得到的a值是多少?
如果你很确信地说答案是:4,那么你可以不用再在这篇文章上浪费时间了,它所说的东西你应该已经掌握了,去其他地方转转吧。但如果你认为答案是7或者其他值,那么就接着往下看吧^_^
为什么main中得到的值是4,而不是7?
首先,try...catch...finally语句中,finally语句块是在try或者catch语句块结束之前执行的。什么叫做结束之前?就是在遇到return或者结束花括号之前。没有return的情况下比较好理解,这里主要说的是遇到return而结束的情况。
对于上面的代码,我们先看下结果7的由来:
- 第一步:从try语句块中【1】执行到【2】,这一过程a的值变为2,在【2】处发生“除零异常”,程序跳去执行catch语句块;
- 第二步:从catch语句块中【4】执行到【5】,这一过程a的值变为4,然后,根据上面所说的,在遇到结束标志return之前,程序跳去执行finally语句块;
- 第三步:从finally语句块中【7】执行到【8】,这一过程a的值变为7,然后,程序又折回到catch语句块中去结束catch块;
- 第四步:执行catch语句块中的【6】,返回a的值7。
对于有返回值的方法,如果返回值是基本类型,那么返回的是基本类型的值;如果返回值是对象类型,那么返回的是对象类型的引用。
对于上面的代码,test()方法返回类型是基本数据类型int,因此返回的是int类型的实际值,而这个值是在执行【6】return之前就已经算出来的,而不是在执行完【8】之后才去计算的。即【5】【6】实际是类似这样的:
System.out.println("catch block. a: " + a);//【5】
int型返回值=a; //【5.5】
return int型返回值; //新【6】
因此,其实应该是在【5.5】之后才转到【7】的,这样经过【8】回到【6】去结束catch时,就不会再计算了,直接返回【5.5】计算得到int值4。
总而言之,你得有一双火眼金睛,要让【5.5】现出原形,不能让【6】蛊惑了。
怎么理解【5.5】的存在性呢?看看下面一段代码:
public class TestException2 {
public static void main(String[] args) {
TestException te = new TestException();
int a = te.test();
System.out.println("call block. a: " + a);
}
public int test()
{
int a = 1;
try
{ //【1】
a = a + 1; //a:2
System.out.println("try block. a: " + a);
//this will cause an exception
//int b = a / 0; //【2】
return a + (1/0); //【3】
}
catch (Exception e)
{//【4】
a = a + 2; //a:4
System.out.println("catch block. a: " + a);//【5】
return a; //【6】
}
finally
{//【7】
a = a + 3; //a:7
System.out.println("finally block. a: " + a);//【8】
//return a; //【9】
}
//return a;
}//eof:test()
}
这段代码和上面第一段代码的区别在于,没有了【2】,改在【3】处产生异常。这段代码,如果按照旧有的思路会推导出自相矛盾的结论的。按照旧有的思路,先执行完【8】,再去【3】计算表达式a + (1/0);的值,那么这个时候计算中出现的“除零异常”就不应该被捕获到了(因为整个try...catch...finally 的 finally 已经执行完,不会再重新执行catch了),而是应该抛出到test()方法外了,即main()方法的调用处了。
至此,对于我们最初的问题,你应该也可以很确信地说答案是4了。
可以对第一段代码稍加修改,自行观察返回值为非基本类型的情况,代码如下:
public class TestException3 {
public static void main(String[] args) {
TestException3 te = new TestException3();
int[] a = te.test();
System.out.println("call block. a[0]: " + a[0]);
}
public int[] test()
{
int[] a = {1};
try
{//【1】
a[0] = a[0] + 1; //a:2
System.out.println("try block. a[0]: " + a[0]);
//this will cause an exception
int b = a[0] / 0; //【2】
return a; //【3】
}
catch (Exception e)
{//【4】
a[0] = a[0] + 2; //a:4
System.out.println("catch block. a[0]: " + a[0]);//【5】
return a; //【6】
}
finally
{//【7】
a[0] = a[0] + 3; //a:7
System.out.println("finally block. a[0]: " + a[0]);//【8】
//return a;//【9】
}
//return a;
}//eof:test()
}