首頁  >  文章  >  Java  >  Java中try與finally以及return語句的執行順序

Java中try與finally以及return語句的執行順序

黄舟
黄舟原創
2017-08-23 10:19:432383瀏覽

這篇文章主要介紹了關於Java中try finally return語句的執行順序淺析,需要的朋友可以參考下

問題分析

finally語句區塊一定會執行嗎?

可能很多人第一反應是肯定要執行的,但仔細一想,如果一定會執行的話 也就不會這麼SB的問了。

Demo1


#
public class Test {
  public static void main(String[] args) {
    System.out.println("return value of test(): " + test());
  }
  public static int test() {
    int i = 1;
    // if (i == 1) {
    // return 0;
    // }
    System.out.println("the previous statement of try block");
    i = i / 0;
    try {
      System.out.println("try block");
      return i;
    } finally {
      System.out.println("finally block");
    }
  }
}

Demo1的執行結果如下:


the previous statement of try block
Exception in thread "main" java.lang.ArithmeticException: / by zero
  at com.becoda.bkms.bus.basics.web.Test2.test(Test2.java:15)
  at com.becoda.bkms.bus.basics.web.Test2.main(Test2.java:5)

另外,如果去掉上例中的註釋,執行結果則是:


#
return value of test(): 0

以上兩種情況,finally語句區塊都沒有執行,說明什麼問題?只有與finally相對應的try語句區塊得到執行的情況下,finally語句區塊才會執行,而上面都是在try語句區塊之前返回(return)或拋出異常,所以try對應的finally語句區塊沒有執行。那麼,即使與finally相對應的try語句區塊得到執行的情況下,finally語句區塊一定會執行嗎?但以下範例

Demo2


#
public class Test {
  public static void main(String[] args) {
    System.out.println("return value of test(): " + test());
  }
  public static int test() {
    int i = 1;
    try {
      System.out.println("try block");
      System.exit(0);
      return i;
    } finally {
      System.out.println("finally block");
    }
  }
}

Demo2的執行結果如下:


#
try block

finally語句區塊還是沒有執行,為什麼呢?因為我們在try語句區塊中執行了System.exit(0)語句,終止了Java虛擬機器的運行,雖然一般情況下我們不會這麼幹。還有情況是當一個執行緒在執行try語句區塊或catch語句區塊時被打斷(interrupted)或被終止(killed),與其對應的finally語句區塊可能不會執行。還有更極端的情況,就是在執行緒執行 try 語句區塊或 catch 語句區塊時,突然當機或斷電,finally 語句區塊肯定不會執行了。

finally 語句範例說明

#下面看一個簡單的範例

Demo3


public class Test {
  public static void main(String[] args) {
    try {
      System.out.println("try block");
      return;
    } finally {
      System.out.println("finally block");
    }
  }
}

Demo3的執行結果為:


try block
finally block

Demo3說明finally 語句區塊在try 語句區塊中的return 語句之前執行。我們再來看另一個例子。

Demo4


#
public class Test {
  public static void main(String[] args) {
    System.out.println("reture value of test() : " + test());
  }
  public static int test() {
    int i = 1;
    try {
      System.out.println("try block");
      i = 1 / 0;
      return 1;
    } catch (Exception e) {
      System.out.println("exception block");
      return 2;
    } finally {
      System.out.println("finally block");
    }
  }
}

Demo4的執行結果為:


try block
exception block
finally block
reture value of test() : 2

Demo4說明了finally 語句區塊在catch 語句區塊中的return 語句之前執行。

從上面的Demo3和Demo4,我們可以看出,其實finally語句區塊時在try或catch中的return語句之前執行的,更加一般的說法是,finally語句區塊應該是在控制轉移語句之前執行,控制轉移語句除了return外,還有break和continue。

再來看下面兩個範例

Demo5


#
public class Test {
  public static void main(String[] args) {
    System.out.println("return value of getValue(): " + getValue());
  }
  public static int getValue() {
    try {
      return 0;
    } finally {
      return 1;
    }
  }
}

Demo5的執行結果為:

return value of getValue(): 1

#Demo6


##

public class Test {
  public static void main(String[] args) {
    System.out.println("return value of getValue(): " + getValue());
  }
  public static int getValue() {
    int i = 1;
    try {
      return i;
    } finally {
      i++;
    }
  }
}

Demo6的執行結果為:


return value of getValue(): 1

利用我們上面分析的結論:finally 語句區塊是在try 或catch 中的return 語句之前執行的。 由此,可以輕鬆的理解Demo5 的執行結果是 1。因為finally 中的return 1;語句要在try 中的return 0;語句之前執行,那麼finally 中的return 1;語句執行後,把程式的控制權轉交給了它的呼叫者main()函數,並且返回值為1。那為什麼Demo6 的回傳值不是 2,而是 1 呢?依照Demo5 的分析邏輯,finally 中的 i++;語句應該在 try 中的 return i;之前執行啊? i 的初始值為 1,那麼執行 i++;之後為 2,再執行 return i;那不就應該是 2 嗎?怎麼變成 1 了呢?

說明這個問題需要了解Java虛擬機器是如何編譯finally語句區塊的。

Java方法是在堆疊幀中執行,堆疊幀是線程私有堆疊的單位,執行方法的執行緒會為每一個方法分配一小塊空間來作為該方法執行時的記憶體空間,堆疊幀分為三個區域:

1、操作數棧,用來保存正在執行的表達式中的操作數

2、局部變數區,用來保存方法中使用的變量,包括方法參數,方法內部聲明的變量,以及方法中使用到的對象的成員變量或類別的成員變量(靜態變量),最後兩個變量會複製到局部變量區,因此在多線程環境下,這種變數需要根據需要宣告為volatile類型

3、字節碼指令區

例如下面這段程式碼


try{
  return expression;
}finally{
  do some work;
}

首先我們知道,finally語句是一定會執行,但他們的執行順序是怎麼樣的呢?他們的執行順序如下:

1、執行:expression,計算該表達式,結果保存在操作數棧頂;

2、執行:操作數棧頂值(expression的結果)複製到局部變數區作為返回值;

3、执行:finally语句块中的代码;

4、执行:将第2步复制到局部变量区的返回值又复制回操作数栈顶;

5、执行:return指令,返回操作数栈顶的值;

我们可以看到,在第一步执行完毕后,整个方法的返回值就已经确定了,由于还要执行finally代码块,因此程序会将返回值暂存在局部变量区,腾出操作数栈用来执行finally语句块中代码,等finally执行完毕,再将暂存的返回值又复制回操作数栈顶。所以无论finally语句块中执行了什么操作,都无法影响返回值,所以试图在finally语句块中修改返回值是徒劳的。因此,finally语句块设计出来的目的只是为了让方法执行一些重要的收尾工作,而不是用来计算返回值的。

这样就能解释Demo6的问题了

让我们再来看以下 3 个例子。

Demo7


public class Test {
  public static void main(String[] args) {
    System.out.println("return value of getValue(): " + getValue());
  }
  @SuppressWarnings("finally")
  public static int getValue() {
    int i = 1;
    try {
      i = 4;
    } finally {
      i++;
      return i;
    }
  }
}

Demo7的执行结果为:


return value of getValue(): 5

Demo8


public class Test {
  public static void main(String[] args) {
    System.out.println("return value of getValue(): " + getValue());
  }
  public static int getValue() {
    int i = 1;
    try {
      i = 4;
    } finally {
      i++;
    }
    return i;
  }
}

Demo8的执行结果为:


return value of getValue(): 5

Demo9


public class Test {
  public static void main(String[] args) {
    System.out.println(test());
  }
  public static String test() {
    try {
      System.out.println("try block");
      return test1();
    } finally {
      System.out.println("finally block");
    }
  }
  public static String test1() {
    System.out.println("return statement");
    return "after return";
  }
}

Demo9的执行结果为:


try block
return statement
finally block
after return

总结:

1、finally 语句块不一定会被执行

2、finally 语句块在 try 语句块中的 return 语句之前执行

3、finally 语句块在 catch 语句块中的 return 语句之前执行

4、finally 语句块中的 return 语句会覆盖 try 块中的 return 返回

5、试图在 finally 语句块中修改返回值不一定会被改变

以上是Java中try與finally以及return語句的執行順序的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn