try...catch语句中return和finally到底谁先执行

写在开头

这个问题真的困扰了我很久,感觉简直像一个哲学(?)问题。

私下和朋友们对这个问题讨论了很久,又在网上查找了很多相关资料,终于还是把这个问题理清楚了。(自认为

我的结论是:在try…catch语句中,当程序执行完return后的表达式后,会转而执行finally语句块,最后再继续执行return

…这个答案看起来是不是还是很哲学?下面我来给出详细解释。

return和finally的定义

首先来看一下 return 和 finally 的定义:

  • return:方法的结束标志,它导致该方法退出,并返回return后的那个值(在返回类型为void的方法里面,也有个被省略的return)。
  • finally:作为异常处理的一部分,它只能用在try…catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。

return作为方法的“结束标志”,也就是说执行完return语句后,方法也就结束了。那么return看起来就是那个最后才执行的语句。

finally表示后面的语句块“最终一定会被执行”,细细品味,似乎只是在说明这段语句块肯定会执行的,但是不是最后才执行的就不一定了。

下面我们用代码来试验一下。

试验代码

定义如下方法,执行并输出

	public static int test() {
		int result = 0;
		try {
			throw new Exception();
		}catch (Exception e) {
			return ++result;
		}finally {
			result += 2;
		}
	}

	public static void main(String[] args) {
		System.out.println("result = " + test());
	}

输出的结果为 result = 1。

这个结果说明 finally 语句块中的内容并没有奏效。通过debug模式来观察这一过程,结果如下:

扫描二维码关注公众号,回复: 4913155 查看本文章
  1. 在 throw new Exception() 处打上断点后,可以看到方法进入了 catch 语句块中,并开始执行 return 语句。此时 result = 0,return 后的表达式还没有执行。
    开始执行return语句

  2. Step Over后,可以看到 result 的值变为 1(执行了 ++result),并开始执行 finally 语句块
    执行return后表达式

  3. 继续Step Over,result 的值变为 3(执行了 result += 2),方法又跳回了 return 语句
    执行finally语句块

  4. 接着,test() 方法执行完毕,可以看到此时方法的返回值为 1
    方法执行完毕

  5. 最后,控制台输出 “result = 1”。
    最后结果

结论

由此可见,执行的顺序是:return 后的表达式(++result) → finally 语句块(result += 2)→ return 返回值

但是为什么 finally 语句块并没有奏效呢?
这是因为当代码执行完 return 语句后的表达式(++result)后,会将要返回的值(result = 1)先存入一个局部变量表(假设这个变量名为 returnedValue,即 returnedValue = 1)。然后再执行 finally 代码块(result += 2),这个代码块修改的是 result 的值而不是 returnedValue 。上面代码中 result 作为返回值,但 result 变量本身与返回值 returnedValue 存放在不同的位置,所以修改了 result 后,returnedValue 并未改变。

补充

这段测试代码中,用来测试的返回值是基本数据类型,最后返回值并没有 finally 语句块被修改。

但是,当返回值为引用类型时,finally 语句块是可以修改最后的返回值的。举个例子:

定义一个雇员类

public class Employee {
	
	private int salary;

	public Employee(int salary) {
		this.salary = salary;//省略getter和setter
}

尝试在 finally 语句块中修改雇员的状态

	public static Employee test() {
		Employee emp = new Employee(100);
		try {
			throw new Exception();
		}catch (Exception e) {
			return emp;
		}finally {
			emp.setSalary(200);
		}
	}

	public static void main(String[] args) {
		System.out.println("emp.salary = " + test().getSalary());
	}

输出结果
补充
这是因为 emp 和 returnedValue 同时指向同一个对象,所以对 emp 的修改会影响到最终的返回值。(具体的原因可以参考我的上一篇文章:Java的参数传递到底是值传递还是引用传递.)

猜你喜欢

转载自blog.csdn.net/weixin_43164759/article/details/86350446