本文学习finally代码块和return执行顺序问题。
finally
finally代码块通常配合try、catch一起出现。 finally块中定义的代码总是在try和任何catch块之后、方法完成之前运行。不管是否抛出或捕获异常 finally 块都会执行。
何时finally代码块不会执行
(1)如果在try代码块之前就执行return,这样finally语句就不会执行,也就说明finally需要被执行就必须进入try代码块。 (2)System.exit(0);这是JVM提供的强制关闭虚拟机,自然finally代码块不会被执行。
例子:
/**
* 在try finally代码块之前return不会执行finally代码块。
*/
@Test
public void testReturnBeforeTry() {
System.out.println("执行业务");
if (true) {
//直接return 通不过编译检查的
return;
}
try {
System.out.println("dosomething");
} finally {
System.out.println("执行finally");
}
}
/**
* 在try finally之前结束虚拟机,不会执行finally代码块
*/
@Test
public void testReturnBeforeExit() {
System.out.println("执行业务");
//结束虚拟机
System.exit(0);
try {
System.out.println("dosomething");
} finally {
System.out.println("执行finally");
}
}
两个例子结果都是一样的,不执行finally代码块。
finally和return
这里两个方面分析,finally代码块和return的代码孰先孰后。finally是在return成功返回之前还是之后执行的。
先说结论
-
finally的代码执行在return之后在方法返回之前。debug查看
-
return的如果是表达式,那么return的值为表达式的结果
/**
* 后加
* @return
*/
public Integer addNext() {
Integer i = 10;
try {
return i++;
} finally {
System.out.println("i的值为=>" + i);
}
}
/**
* 后加
* @return
*/
public Integer addPrev() {
Integer i = 10;
try {
return ++i;
} finally {
System.out.println("i的值为=>" + i);
}
}
/**
* 表达式
* @return
*/
public Integer addExpression() {
Integer i = 10;
try {
return ++i;
} finally {
System.out.println("i的值为=>" + i);
}
}
@Test
public void test3() {
Demo01 demo01 = new Demo01();
System.out.println(demo01.addNext());
System.out.println(demo01.addPrev());
System.out.println(demo01.addExpression());
}
这个例子说明了
①return 对于基本数据类型(包括其包装类型),其返回值由表达式决定
finally代码块中return会怎样?
/************在final代码块中return,会覆盖之前的return****************/
public String returnInFinally() {
try {
System.out.println("try执行");
return "try return";
} finally {
System.out.println("finally执行");
return "finally return";
}
}
@Test
public void test() {
Demo01 demo01 = new Demo01();
System.out.println(demo01.returnInFinally());
}
这个简单例子只为说明,finally中的return会覆盖try中的return。且不建议在finally中retuen。
finaly可以影响try中的return值吗?
此处意思是如果finally对return的结果进行修改会不会影响return结果。
理解关键在于 java的值传递。对于基本数据类型只有值,对于引用类型传递的是引用地址。
第一种情况:finally不会改变return结果
关于基本数据类型的包装类型和String,如果修改他们的值会直接改变引用地址。 这个是jvm内存模型,简单理解。
如上图
比如 Integer i = 10; i = i + 10;
其实这个加操作会做哪些事呢?
①首先会在堆内存创建一个新的Integer对象 20
②将新建的Integer对象的内存地址赋给i
public int finallyModifyInt(int i) {
try {
System.out.println("try 原结果=>" + i);
return i;
} finally {
if (i >= 16) {
i = i & ((1 << 4) - 1);
System.out.println("finally 修改后=>" + i);
}
}
}
@Test
public void test5() {
Demo01 demo01 = new Demo01();
System.out.println("最终结果=>" + demo01.finallyModifyInt(20));
}
这里可以得出一个结论,关于基本数据类型return的时候会记录被return结果的值,而对于基本数据类型的包装类型以及String类型,由于他们是不可变的也就是没有提供修改当前属性值的Api,所以Return的是啥就是啥,finally代码块并不会影响return的结果。
finally会修改return结果:
简单理解
/****对于可变的引用类型,由于他们1⃣️属于引用类型2⃣️提供了修改当前对象的Api。所以在finally中对对象的修改,会影响return的结果****/
@Data
@AllArgsConstructor
class Person {
Integer age;
String name;
}
public Person modifyPerson(Person p) {
try {
System.out.println("try 原结果=>" + p);
return p;
} finally {
if (p.getName().equals("abc")) {
p.setName("123");
System.out.println("finally 修改后=>" + p);
}
}
}
@Test
public void test52() {
Demo01 demo01 = new Demo01();
System.out.println("最终结果=>" + demo01.modifyPerson(new Person(23, "abc")));
}
可以发现修改了return的结果,对于引用类型虽然return记录了return结果的引用地址值,但是
所指向的对象是可以被修改的。
接下来还有一些情况,比如说在catch中的操作对return的影响,或者在catch中return,当然catch专心做捕获异常工作,一般做一些日志记录。
总结
-
finally和return的代码执行顺序
finally代码块的执行在return代码执行之前,在方法返回之后
-
return的结果,是否受finally影响
对于基本数据类型和不可变的数据类型,finally不会影响return的结果。对于提供修改属性api的对象,finally可以影响return的返回结果。总之return的时候会记住需要返回的值(对于基本数据类型来说就是值,对于引用数据类型来说就是引用地址)