关于异常捕捉中含有return的子句与finally子句执行顺序问题
參考文章1java中finally与return的执行顺序详解
一句话结论
finally一定会执行,而且先于return子句执行但是晚于try或者会被执行的catch子句
而且由于return (exps) ,先完成exps(即表达式),再将表达式的 { 结 果 } 放在栈顶,最后将栈顶返回,所以finally子句对于返回对象(由于Java是值传递,引用类型只存所指向的地址,这个地址 就是 { 结 果 })的值进行的操作是无法影响try或者catch中return的值,因为已经计算完成放在栈顶了(程序第7行,值传递在本片末尾对于参考文章2有解释)
但是finally子句中有return则优先,可以理解为重新执行了return的三步,也就是先完成表达式,再将表达式结果放在栈顶,最后返回栈顶,等于将栈顶进行了更新
例子:将第13行解除注释,将15行注释(不可被执行错误,属于compile错误),发现结果输出了7
#
代码块
package test1;
public class ThrowsException {
public int say(int a) {
try {
System.out.println("trying...");
return a;
} catch (RuntimeException e) {
System.out.println("catching...");
} finally {
a = 7;
System.out.println("finally...");
//return a;
}
return 0;
}
public static void main(String[] args) {
System.out.println("hello:" + new ThrowsException().say(6));
}
}
运行结果
trying... //try子句执行
finally... //先于try的return执行finally
hello:6//方法执行完毕,return返回了结果6,将第13行解除注释,将15行注释将输出7
引用来自http://blog.csdn.net/qj19842011/article/details/45675057#reply的文章
首先为了说明白java中finally与return的执行顺序是怎样的这个问题,我们需要做一点准备工作。
java方法是在栈幀中执行,栈幀是线程私有栈的单位,执行方法的线程会为每一个方法分配一小块栈空间来作为该方法执行时的内存空间,栈幀分为三个区域:
1. 操作数栈,用来保存正在执行的表达式中的操作数,数据结构中学习过基于栈的多项式求值算法,操作数栈的作用和这个一样
2. 局部变量区,用来保存方法中使用的变量,包括方法参数,方法内部声明的变量,以及方法中使用到的对象的成员变量或类的成员变量(静态变量),最后两种变量会复制到局部变量区,因此在多线程 环境下,这种变量需要根据需要声明为volatile类型
3. 字节码指令区,这个不用解释了,就是方法中的代码翻译成的指令
return语句:
准备知识讲完了,在本节中我们讲解方法中没有finally语句块的情况下return语句的执行方式。现在我们先看看return语句,return语句的格式如下:
return [expression];
其中expression(表达式)是可选的,因为有些方法没有返回值,所以return后面也就没有表达式,或者可以看做是空的表达式。
我们知道return语句的作用可以结束方法并返回一个值,那么他返回的是哪里的值呢?返回的是return指令执行的时刻,操作数栈顶的值,不管expression是一个怎样的表达式,究竟做了些什么工作,对于return指令来说都不重要,他只负责把操作数栈顶的值返回。
而return expression是分成两部分执行的:
执行:expression;
执行:return指令;
例如:return x+y;
这句代码先执行x+y,再执行return;首先执行将x以及y从局部变量区复制到操作数栈顶的指令,然后执行加法指令,这个时候结果x+y的值会保存在操作数栈的栈顶,最后执行return指令,返回操作数栈顶的值。
关于参考文章2的涉及值传递的一些解释,在注释中
import java.util.*;
public class FinallyTest6
{
public static void main(String[] args) {
System.out.println(getMap().get("KEY").toString());
}
public static Map<String, String> getMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("KEY", "INIT");
try {
map.put("KEY", "TRY");
return map;//返回的是指向原参数的引用,注意 原参!!!不是形参!!!
}
catch (Exception e) {
map.put("KEY", "CATCH");
}
finally {
map.put("KEY", "FINALLY");//改变了原参数
map = null;//值传递,仅仅改变形参,不改变原参数,形参早就计算完成并将结果放在操作数栈顶了,这里没有改变栈顶的数,符合上面总结的改变返回对象的值(也就是形参)的操作,不改变try的return值
}
return map;
}
}