部分demo代码来自https://blog.csdn.net/jdfk423/article/details/80406297#commentsedit
私以为原文结论不太准确,也不太完善,以下是个人见解。
问题1:try和catch中有return时,finally里面的语句会被执行吗
(1) 执行try中的return时
public class TryDemo {
public static int show() {
try {
return 1;
}finally{
System.out.println("finally模块被执行");
}
}
public static void main(String args[]) {
System.out.println(show());
}
}
输出结果如下:
(2) 执行catch中的return时
public class TryDemo {
public static int show() {
try {
int a = 8/0;
return 1; // 这里因为异常发生,此行代码不会被执行
}catch (Exception e) {
// 从结果上看,这行代码被执行了,那么是它先执行,还是finally先执行呢?结论看问题2
return 2;
}finally{
System.out.println("finally模块被执行");
}
}
public static void main(String args[]) {
System.out.println(show());
}
}
其结果如下:
我们可以得出结论:
结论1:如果程序是从try代码块或者catch代码块中返回时,finally中的代码总会执行。
引申:finally代码什么时候不会被执行?
答案:
1.如果try语句或者catch语句里存在强制退出语句System.exit(0),代表虚拟机被终止。
2.在执行try...catch语句前已经有了return语句返回,后面的程序不再被执行。
问题2:try代码块或者catch代码块中的return语句 和 finally中的代码谁先被执行?
demo1 返回值是基本数据类型
public class TryDemo {
public static int show() {
int result = 0;
try {
return result;
}finally{
System.out.println("finally模块被执行");
result = 1;
}
}
public static void main(String args[]) {
System.out.println(show());
}
}
demo2 返回值是引用数据类型
public class TryDemo {
public static Object show() {
Object obj = new Object();
try {
return obj;
}finally{
System.out.println("finally模块被执行");
obj = null;
}
}
public static void main(String args[]) {
System.out.println(show());
}
}
结果分别如下
我们可以得出结论:
结论2:try代码块或者catch代码块中的return语句 优先于finally中的代码块执行,且函数真正返回之前,finally中的代码块执行必须先执行,所以可以这样说,finally语句在return语句执行之后return返回之前执行的。
到这里还没有结束,需要注意:虽然finally中的语句(非return语句)不会影响最终返回值,但它对返回值的修改其实是生效了的,demo1中因为是基本数据类型,所以看不出来,demo2中返回值是new Object这个对象的地址引用,它并没有被修改,修改的只是obj这个局部变量的指向,原先的对象本身并没有发生改变,所以说demo2其实不太ok。
这里我先贴出一个结论,结论来自于下文中我推荐的博客:
总结:对于finally块中没有return语句的情况,方法在返回之前会先将返回值保存在局部变量表中的某个slot中,然后执行finally块中的语句,之后再将保存在局部变量表中某个slot中的数据放入操作数栈的栈顶并进行返回,因此对于基本数据类型而言,若在finally块中改变其值,并不会影响最后return的值,而若是引用数据类型,改变对象的内容信息是会作用到最后的返回值上的(因为返回值实际并不是对象本身,而是其引用)。
而对于finally块中包含了return语句的情况,则在try块中的return执行之后返回之前,会先goto到finally块中,而在goto之前并不会对try块中要返回的值进行保护,而是直接去执行finally块中的语句,并最终执行finally块中的return语句并直接返回,而忽略try块中的return语句的返回,因此最终返回的值是在finally块中改变之后的值。
demo3
public class TryDemo {
public static People show() {
People p = new People(18);
try {
return p;
} finally {
System.out.println("finally模块被执行");
p.setAge(20);
}
}
public static void main(String args[]) {
System.out.println(show().getAge());
}
}
class People {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public People(int age) {
super();
this.age = age;
}
}
结果如图:
问题3:如果finally中也有return的时候,finally块中包含的return语句对返回值会有怎样的影响
public class TryDemo {
public static int show() {
try {
int a = 8/0;
return 1;
}catch (Exception e) {
return 2;
}finally{
System.out.println("finally模块被执行");
return 0;
}
}
public static void main(String args[]) {
System.out.println(show());
}
}
执行结果如下:
结论:我们可以看到当finally有返回值时,会选择返回finally语句中的返回值,不会再去返回try或者catch中的返回值。另外我通过debug发现,try或者catch语句中的return语句还是有被执行的,但是在最终选择返回值时,选择了执行finally语句中的。
对于问题2和问题3的进一步深入学习了解,对于以上几个demo更好的解释我推荐这篇博文,从字节码指令层次分析https://blog.csdn.net/abinge317/article/details/52253768