一直以为自己对try catch足够了解了,但是在看到这篇博客后才意识到自己了解的仅仅是皮毛。
首先要知道f inally不是一定会执行的.下面的情况下finally就肯定不会执行的。
(1)try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行, 这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。
(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。
其次是finally究竟是什么时候执行。
finally语句是在try的return语句执行之后,return返回之前执行。这句话理解起来很费劲,我们下面用debug的截图解释。
在执行finally代码时,如果finally代码本身没有退出的语句(return或抛出异常),finally执行完毕后还会返回try或catch,由try或catch执行退出指令。
try一定要配合catch或者finally一起使用
当finally中包含return时,就不能在try和finally之后写return了,只能在catch中写return了,否则会编译通不过。
我们先看看下面的代码:
package com.xfl.exception;
/**
* Created by XFL.
* time on 2016/12/3 15:14
* description:异常捕获测试
* 在执行finally代码时,如果finally代码本身没有退出的语句(return或抛出异常),
* finally执行完毕后还会返回try或catch,由try或catch执行退出指令
*/
public class TestException {
public static void main(String[] args) {
int result1 = test1();
int result2 = test2();
System.out.println("最终的结果是: " + result1);
System.out.println("最终的结果是: " + result2);
}
/**
* 出异常返回3,不出异常返回4.finally是在return之前执行的
*
* @return
*/
private static int test1() {
int temp = 1;
try {
temp += 1;
int a = 1;
int b = (Math.random() * 10) % 3 > 1 ? 1 : 0;
int c = a / b;
} catch (Exception e) {
temp += 1;
System.out.println(temp);
/**
* Return并不是让函数马上返回,而是return语句执行后,将把返回结果放置进函数栈中,
* 此时函数并不是马上返回,它要执行finally语句后才真正开始返回。
* finally语句是在try的return语句执行之后,return返回之前执行
*/
return temp;
} finally {
/**
* finally不是一定会执行的.
* (1)try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,
* 这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。
*(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,
* 连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。
*/
temp += 1;
System.out.println(temp);
}
temp += 1;
System.out.println(temp);
return temp;
}
/**
* 出异常返回4,不出异常返回3.finally是在return之前执行的
*
* @return
*/
private static int test2() {
int temp = 1;
try {
temp += 1;
int a = 1;
int b = (Math.random() * 10) % 3 > 1 ? 1 : 0;
int c = a / b;
} catch (Exception e) {
temp += 1;
//执行return之前会先执行finally
System.out.println(temp);
return temp;
} finally {
temp += 1;
//finally中包含return时catch中的return不起作用了
System.out.println(temp);
return temp;
}
}
}
我们分析test1方法出异常时的执行情况,出错时执行catch
此时catch中包含return语句,return并不是让函数马上返回,而是return语句执行后,将把返回结果放置进函数栈中,然后再执行finally部分
这里temp的值已经变成4了,执行完finally部分后,因为finally没有退出程序的语句,所以会返回到catch中。
虽然temp已经变成了4,但是真正返回的是3,因为这个return已经执行过了,前面已经将返回结果放到栈中了,这个时候执行return只是把栈中的值返回。(注意这里需要根据返回类型区分,如果是引用类型则在finally中对返回数据的修改是启作用的,如果是基本数据类型就不启作用了,猜测原因应该是Java的值传递,当是引用类型的时候,返回的并不是对象的内容,而是对象的引用)
现在来分析test2方法,此方法finally中包含了return语句
执行到catch中的return语句时先把返回值放入到栈中,再执行finally语句
执行到finally的return语句时,也会把返回值放入到栈中,这样前面的就不起作用了,因为finally包含程序退出语句,所以就不会再像test1方法中那样再次执行catch中的return了。这里真正返回的是4,而不是3.
上图是两个方法程序运行的结果。
现在我们来了解下Java Stacks。
所谓Java栈,描述的是一种Java中方法执行的内存模型,Java栈为线程私有,线程中每一次的方法调用(或执行),JVM都会为该方法分配栈内存,即:栈帧(Stack Frame),分配的栈帧用于存放该方法的局部变量表、操作栈(JVM执行的所有指令都是围绕它来完成的)、方法编译后的字节码指令信息和异常处理信息等,JVM指定了一个线程可以请求的栈深度的最大值,如果线程请求的栈深度超过这个最大值,JVM将会抛出StackOverflowError,注意,此处抛出的时Error而不是Exception。