try、catch、finally、return 执行顺序超详解析(针对面试题)
有关 try、catch、finally 和 return 执行顺序的题目在面试题中可谓是频频出现。总结一下此类问题几种情况。
写在前面
- 不管 try 中是否出现异常,finally 块中的代码都会执行;
- 当 try 和 catch 中有 return 时,finally 依然会执行;
- finally 是在 return 语句执行之后,返回之前执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管 finally 中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以如果 finally 中没有 return,即使对数据有操作也不会影响返回值,即如果 finally 中没有 return,函数返回值是在 finally 执行前就已经确定了;
- finally 中如果包含 return,那么程序将在这里返回,而不是 try 或 catch 中的 return 返回,返回值就不是 try 或 catch 中保存的返回值了。
注:
- finally 修改的基本类型是不影响 返回结果的。(传值)
- 修改 list,map,自定义类等引用类型时,是影响返回结果的。(传址的)对象也是传址的
但是 date 类型经过测试是不影响的。
1、try{} catch(){}finally{} return;
按程序顺序运行,如果 try 中有异常,会执行 catch 中的代码块,有异常与否都会执行 finally 中的代码;最终返回。
2、try{return;}catch(){} finally{} return;
- 先执行 try 块中 return 语句(包括 return 语句中的表达式运算),但不返回;
- 执行 finally 语句中全部代码
- 最后执行 try 中 return 返回
finally 块之后的语句 return 不执行,因为程序在 try 中已经 return。
示例:
输出是
finally语句块 和是:43
至于为什么不是:和是 finally 块 43 的原因:
System.out.println(“和是:”+test.add(9,34)); 这是进行字符串拼接是一个整体,所以首先是进入 add 方法,进去之后先不运算 result,而是输出 finally 块。finally 语句块,这句话先打印到控制台中。打印完后返回来执行 try 中的 return 得到 43,此时再将结果与和是:拼接,输出和是:43. 所以最终输出 finally 语句块 和是:43。
3、try{} catch(){return;} finally{} return;
程序先执行 try,如果遇到异常执行 catch 块,最终都会执行 finally 中的代码块;
- 有异常:
- 执行 catch 中的语句和 return 中的表达式运算,但不返回
- 执行 finally 语句中全部代码,
- 最后执行 catch 块中 return 返回。 finally 块后的 return 语句不再执行。
- 无异常:执行完 try 再 finally 再 return…
示例 1:有异常
输出:1132
先执行 try 中的打印 temp=1;有异常,执行 catch 中的打印,然后执行 return 中的表达式运算,此时 temp=2,并将结果保存在临时栈中,但不返回;执行 finally 中的语句,temp++,此时 temp 更新为 3,同时打印,但因为 finally 中的操作不是 return 语句,所以不会去临时栈中的值,此时临时栈中的值仍然是 2。finally 中的执行完后,执行 catch 中的返回,即 2。
示例 2:无异常
输出:123
4、try{return;}catch(){} finally{return;}
- 执行 try 块中的代码,和 return 语句(包括 return 语句中的表达式运算),但不返回(try 中 return 的表达式运算的结果放在临时栈);
- 再执行 finally 块,
- 执行 finally 块(和 return 中的表达式运算,并更新临时栈中的值),从这里返回。
此时 finally 块的 return 值,就是代码执行完后的值
5、try{} catch(){return;}finally{return;}
- 执行 try 中的语句块,
- 有无异常
- 有异常:程序执行 catch 块中 return 语句(包括 return 语句中的表达式运算,,并将结果保存到临时栈),但不返回;
- 无异常:直接执行下面的
- 再执行 finally 块,
- 执行 finally 块 return 中的表达式运算,并更新临时栈中的值,并从这里返回。
示例 1:无异常:
输出:112
示例 2:有异常
输出:1123
6、try{return;}catch(){return;} finally{return;}
程序执行 try 块中 return 语句(包括 return 语句中的表达式运算),但不返回;
- 有异常:
- 执行 catch 块中的语句和 rreturn 语句中的表达式运算,但不返回,结果保存在临时栈;
- 再执行 finally 块
- 执行 finally 块,有 return,更新临时栈的值,并从这里返回。
- 无异常:
- 直接执行 finally 块
- 执行 finally 块,有 return,更新临时栈的值,并从这里返回。
示例 1:无异常:
输出:123
示例 2:有异常
输出:1123
7、try{return;}catch(){return;} finally{其他}
程序执行 try 块中 return 语句(包括 return 语句中的表达式运算),但不返回;
- 有异常:
- 执行 catch 块中 return 语句(包括 return 语句中的表达式运算),但不返回;
- 再执行 finally 块
- 无异常:
- 再执行 finally 块
- 执行 finally 块,有 return,从这里返回。
示例:
输出结果为 132
执行顺序为:
- 输出 try 里面的初始 temp:1;
- temp=2;
- 保存 return 里面 temp 的值:2;
- 执行 finally 的语句 temp:3,输出 temp:3;
- 返回 try 中的 return 语句,返回存在里面的 temp 的值:2;
- 输出 temp:2。
finally 代码块在 return 中间执行。return 的值会被放入临时栈,然后执行 finally 代码块,如果 finally 中有 return,会刷新临时栈的值,方法结束返回临时栈中的值。
最终结论:
- 任何执行 try 或者 catch 中的 return 语句之后,在返回之前,如果 finally 存在的话,都会先执行 finally 语句,
- 如果 finally 中有 return 语句,那么程序就 return 了,所以 finally 中的 return 是一定会被 return 的,
- 编译器把 finally 中的 return 实现为一个 warning。
- 不管有没有异常,finally 代码块(包括 finally 中 return 语句中的表达式运算)都会在 return 之前执行
- 多个 return(中的表达式运算)是按顺序执行的,多个 return 执行了一个之后,后面的 return 就不会执行。不管 return 是在 try、catch、finally 还是之外。