關於 try 和 finally 中的 return

NO IMAGE
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

關於 try 和 finally 中的 return

首先我們來看一段程式碼:

public class Test {

    public static int inc() {
        int x = 1;

        try {
            return   x; // 1*
        } catch (Exception e) {
            
        } finally {
            x  ;
        }

        return x;
    }

    public static void main(String[] args) {
        System.out.println(inc());
    }
}

它的輸出結果是多少呢?

2

我們走一下這個過程,x 的初始值是 1,然後進入到了 try 語句塊中,在 1* 處, x,x 會先自增,現在 x = 2,之後 return,return 是用來跳出當前方法,而 finally 是無論 try 語句發生了什麼,都會執行的一個語句塊,那麼 try 中 return 和 finally 執行的順序到底是誰先誰後呢?

我們來看 Oracle 文件中對 finally Block 的描述 The finally Block (The Java™ Tutorials > Essential Classes > Exceptions)

The finally Block

The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

Note: If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.

從這段解釋中我們可以知道,當 try 語句塊退出時,finally 語句塊總是會執行,這保證了當有異常發生時,finally 語句塊會被執行,不過 finally 語句塊的作用不僅於此,它幫助程式設計師避免在執行 return or continue or break 時繞過清理程式碼,所以即使沒有異常需要捕獲,將清理程式碼放到 finally 語句塊中也是一個好的選擇。

需要注意的是,只有一種情況:如果在執行 try or catch 語句時 JVM 退出了,比如我們呼叫 System.exit,那麼 finally 才不會被執行。

在 Java 語言規範 Chapter 14. Blocks and Statements 中也提到:

The preceding descriptions say “attempts to transfer control” rather than just “transfers control” because if there are any try statements (§14.20) within the method or constructor whose try blocks or catch clauses contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.

如果在 try or catch 語句塊中包含 return 語句,那麼 finally 語句會在其 return 之前執行。

根據以上描述,我們知道了 finally 語句塊會在 try or catch 語句塊執行前執行,那麼當 x 自增後,會繼續執行 finally 語句塊中的內容,即 x ,那麼此時 x = 3,可是這段程式的返回結果是 2 ,這又是為什麼呢?

我們來看 JVM 規範中的描述 Chapter 4. The class File Format

Control can be transferred to the finally clause (the finally subroutine can be invoked) in several different ways. If the try clause completes normally, the finally subroutine is invoked via a jsr instruction before evaluating the next expression. A break or continue inside the try clause that transfers control outside the try clause executes a jsr to the code for the finally clause first. If the try clause executes a return, the compiled code does the following:

  1. Saves the return value (if any) in a local variable.
  2. Executes a jsr to the code for the finally clause.
  3. Upon return from the finally clause, returns the value saved in the local variable.

也就是說,如果 try 語句中包含 return,那麼編譯後的程式碼會執行以下操作:

  1. 將 return 的值存到一個區域性變數中
  2. 執行 jsr 指令到 finally 語句塊中的程式碼
  3. 從 finally 語句返回時,返回在區域性變數中儲存的值

終於,謎團揭開了!原來在 finally 語句中執行完畢後,它會返回存在區域性變數中的在 try 語句塊中 return 的值,因此它返回的是 1* 處的 x,也就是返回的 2。

並且還需要注意的是,finally 中的 return 會覆蓋 try 中的 return。

參考資料:

http://www.cnblogs.com/averey…
https://docs.oracle.com/javase

相關文章

程式語言 最新文章