關於 try 和 finally 中的 return

關於 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/p/4379646.html
https://docs.oracle.com/javase

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章