近期在重构一个功能,发现代码中途可能会直接return,但是重构优化的时候要考虑其他的问题并处理异常,那么问题来了:Java里面try-catch-finally中,如果在try里面直接return,finally的代码会不会执行?
答案是:会,finally块中的代码一定会执行。

即使在try块中使用了return语句,Java也会保证在方法真正返回之前,先去执行finally块中的代码。

详细解释和执行顺序

  1. 执行try块:代码正常执行到return语句。
  2. 计算返回值:计算return语句后面的表达式的值,并将这个返回值暂存在一个临时变量中。此时,方法并没有立即返回。
  3. 执行finally块:Java转而开始执行finally块中的所有代码。
  4. 方法返回: finally块执行完毕后,方法再将之前暂存的返回值返回出去。

关键点:返回值是如何被影响的?

这里有一个非常重要的细微差别:如果finally块里也有return语句,它会覆盖try(或catch)块中的返回值。这通常被认为是一种不好的实践,因为它会掩盖try块中的原始返回意图,并且使代码难以理解。

让我们通过几个例子来加深理解:


示例1:finally执行,但不影响基本类型的返回值

public class FinallyExample {
    public static void main(String[] args) {
        System.out.println("Result: " + test());
    }

    public static int test() {
        try {
            System.out.println("Try block");
            return 1; // 返回值 1 被暂存
        } finally {
            System.out.println("Finally block");
            //  finally 执行,但这里没有 return
        }
    }
}

输出:

Try block
Finally block
Result: 1

过程: return 1; 执行,值1被暂存 -> 执行finally -> 返回暂存的值1


示例2:finally中的return覆盖了try中的返回值(不推荐!)

public class FinallyExample {
    public static void main(String[] args) {
        System.out.println("Result: " + test());
    }

    public static int test() {
        try {
            System.out.println("Try block");
            return 1;
        } finally {
            System.out.println("Finally block");
            return 2; // 这个 return 会覆盖 try 中的 return
        }
    }
}

输出:

Try block
Finally block
Result: 2

过程: return 1; 执行,值1被暂存 -> 执行finally -> 遇到return 2;,值2成为新的返回值 -> 方法返回2,之前暂存的1被丢弃。


示例3:finally修改引用类型对象的内部状态

public class FinallyExample {
    public static void main(String[] args) {
        System.out.println("Result: " + test().getValue());
    }

    public static MyObject test() {
        MyObject obj = new MyObject();
        obj.setValue(10);
        try {
            System.out.println("Try block");
            obj.setValue(20);
            return obj; // 返回的是对象引用(地址)的副本
        } finally {
            System.out.println("Finally block");
            obj.setValue(30); // 通过引用修改对象内部的状态
        }
    }

    static class MyObject {
        private int value;
        public int getValue() { return value; }
        public void setValue(int value) { this.value = value; }
        @Override
        public String toString() { return "MyObject{value=" + value + '}'; }
    }
}

输出:

Try block
Finally block
Result: 30

过程:

  1. return obj; 执行时,返回的是对象obj引用地址的副本,并暂存。
  2. 执行finally块,通过这个引用(obj)修改了对象的内部状态(value从20变为30)。
  3. 方法返回的是之前暂存的那个引用地址,而通过这个地址找到的对象,其value已经被finally修改为30了。

总结

场景 finally是否执行 说明
try 中有 return 先暂存返回值,执行finally,再返回。
tryfinally 中都有 return finally中的return会覆盖try中的return
try 中抛出异常 先执行finally,再将异常抛出给调用者。
try 中调用 System.exit(0) System.exit(0)会直接终止JVM进程。
守护线程因所有非守护线程结束而终止 可能否 如果JVM在finally执行前就退出了,则不会。

最佳实践:

  • 避免在finally块中使用return语句,这会掩盖try和catch块中的异常和返回语句,导致难以调试的bug。
  • finally块通常用于释放资源,如关闭文件流、数据库连接等,以确保资源在任何情况下都能被正确清理。

更多推荐