杰瑞科技汇

finally里的return会覆盖前面的return吗?

Of course! This is a classic and often misunderstood topic in Java. Let's break down the finally block and its interaction with return statements thoroughly.

finally里的return会覆盖前面的return吗?-图1
(图片来源网络,侵删)

The Golden Rule

A return statement inside a try or catch block does not exit the method immediately if a finally block is present. The finally block is always executed before the method actually returns.

Think of it this way: the finally block has the "last say" on what value is returned, if any.


How it Works: The Execution Flow

When a return statement is encountered, the JVM does the following:

  1. It evaluates the expression to be returned and stores the result in a temporary, hidden variable. Let's call it return_value.
  2. It then executes the code inside the finally block.
  3. After the finally block has completed, it takes the return_value from step 1 and uses that to exit the method.

This is why the finally block can "intercept" a return from the try or catch block.

finally里的return会覆盖前面的return吗?-图2
(图片来源网络,侵删)

Code Examples

Let's look at several scenarios to solidify this understanding.

Scenario 1: return in try, finally has no return

This is the most common case. The finally block runs, but the original return value from the try block is honored.

public class FinallyReturnExample {
    public static int tryFinallyExample() {
        int result = 10;
        try {
            System.out.println("Try block: Returning " + result);
            return result; // 1. Store result (10) to be returned.
        } finally {
            System.out.println("Finally block: Executing...");
            // 2. This code runs.
            result = 20; // This change is local to the method, it doesn't affect the stored return value.
            System.out.println("Finally block: result is now " + result);
        }
        // 3. This line is never reached because the return in the try block causes control to jump to the finally block.
        // System.out.println("This will not print.");
    }
    public static void main(String[] args) {
        int value = tryFinallyExample();
        System.out.println("Method returned: " + value); // What will this print?
    }
}

Output:

Try block: Returning 10
Finally block: Executing...
Finally block: result is now 20
Method returned: 10

Explanation:

finally里的return会覆盖前面的return吗?-图3
(图片来源网络,侵删)
  1. The try block sets result to 10 and hits return result;. The JVM says, "Okay, I need to return 10 eventually, but first, I must run the finally block."
  2. The finally block executes. It changes the result variable to 20, but this is too late. The value 10 has already been set aside for the return.
  3. After the finally block finishes, the method returns the stored value, which is 10.

Scenario 2: return in try, finally also has a return

This is where it gets critical. If the finally block also has a return statement, it completely overrides the return from the try block.

public class FinallyReturnOverrideExample {
    public static int tryFinallyOverrideExample() {
        try {
            System.out.println("Try block: Returning 10");
            return 10; // The JVM says, "I'll return 10... unless..."
        } finally {
            System.out.println("Finally block: Executing and overriding the return...");
            return 30; // This return statement takes precedence.
        }
    }
    public static void main(String[] args) {
        int value = tryFinallyOverrideExample();
        System.out.println("Method returned: " + value); // What will this print?
    }
}

Output:

Try block: Returning 10
Finally block: Executing and overriding the return...
Method returned: 30

Explanation:

  1. The try block hits return 10;. The JVM notes this but doesn't exit yet.
  2. The finally block is executed.
  3. The finally block encounters its own return 30;. This becomes the definitive return value for the entire method.
  4. The original return 10; is effectively discarded.

Scenario 3: return in catch, finally has a return

The same rule applies when the return is in a catch block.

public class FinallyCatchOverrideExample {
    public static int catchFinallyOverrideExample() {
        try {
            int x = 10 / 0; // This will throw an ArithmeticException
            return 10;
        } catch (Exception e) {
            System.out.println("Catch block: Caught exception, Returning 20");
            return 20; // The JVM says, "I'll return 20... unless..."
        } finally {
            System.out.println("Finally block: Executing and overriding the return...");
            return 40; // This return statement takes precedence.
        }
    }
    public static void main(String[] args) {
        int value = catchFinallyOverrideExample();
        System.out.println("Method returned: " + value);
    }
}

Output:

Catch block: Caught exception, Returning 20
Finally block: Executing and overriding the return...
Method returned: 40

Best Practice and Warning

Do not put a return statement inside a finally block.

While the language allows it, it is almost always a terrible idea because it leads to confusing and non-obvious code. It hides the original flow of the method and can make debugging a nightmare. Anyone reading your code will expect the finally block to be for cleanup, not for dictating the return value.

What about System.exit()?

There is one exception to the "always execute finally" rule: System.exit().

Calling System.exit() terminates the entire Java Virtual Machine (JVM). If a finally block contains a call to System.exit(), the finally block will start to execute, but the JVM will shut down before any code after the System.exit() call in the finally block can run. Crucially, it will prevent any return statements from the try or catch blocks from being honored.

public class SystemExitExample {
    public static void systemExitFinally() {
        try {
            System.out.println("Try block: About to return 10");
            return 10;
        } finally {
            System.out.println("Finally block: About to exit the JVM!");
            System.exit(0); // This terminates the program immediately.
            System.out.println("This line will never be printed.");
        }
    }
    public static void main(String[] args) {
        // The program will terminate here, and the return from the try block is ignored.
        systemExitFinally();
        System.out.println("This line in main will also never be printed.");
    }
}

Output:

Try block: About to return 10
Finally block: About to exit the JVM!

The program then terminates.

Summary Table

Scenario finally Block Executes? Return Value
return in try, no return in finally Yes The value from the try block's return.
return in try, return in finally Yes The value from the finally block's return. (Overrides the try)
return in catch, return in finally Yes The value from the finally block's return. (Overrides the catch)
return in try/catch, System.exit() in finally Yes, partially The program terminates. The return from try/catch is ignored.
No exception thrown, no return in try/catch Yes N/A (Method continues after finally block)
Exception thrown, not caught by a catch block Yes The exception propagates out of the method after finally runs.
分享:
扫描分享到社交APP
上一篇
下一篇