finally代码块在return前执行?

2,079 阅读4分钟

本文学习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代码块。 image.png

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());
}

image.png 这个例子说明了
①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());
}

image.png

这个简单例子只为说明,finally中的return会覆盖try中的return。且不建议在finally中retuen。


finaly可以影响try中的return值吗?

此处意思是如果finally对return的结果进行修改会不会影响return结果。
理解关键在于 java的值传递。对于基本数据类型只有值,对于引用类型传递的是引用地址。

第一种情况:finally不会改变return结果

关于基本数据类型的包装类型和String,如果修改他们的值会直接改变引用地址。 这个是jvm内存模型,简单理解。

image.png

如上图 比如 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));
}

image.png

这里可以得出一个结论,关于基本数据类型return的时候会记录被return结果的值,而对于基本数据类型的包装类型以及String类型,由于他们是不可变的也就是没有提供修改当前属性值的Api,所以Return的是啥就是啥,finally代码块并不会影响return的结果。

finally会修改return结果:

简单理解

image.png

/****对于可变的引用类型,由于他们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")));

}

image.png 可以发现修改了return的结果,对于引用类型虽然return记录了return结果的引用地址值,但是 所指向的对象是可以被修改的。

接下来还有一些情况,比如说在catch中的操作对return的影响,或者在catch中return,当然catch专心做捕获异常工作,一般做一些日志记录。


总结

  • finally和return的代码执行顺序

    finally代码块的执行在return代码执行之前,在方法返回之后

  • return的结果,是否受finally影响

    对于基本数据类型和不可变的数据类型,finally不会影响return的结果。对于提供修改属性api的对象,finally可以影响return的返回结果。总之return的时候会记住需要返回的值(对于基本数据类型来说就是值,对于引用数据类型来说就是引用地址)