首先來問你們一個問題:finally 語句塊必定會執行嗎?java
不少人都認爲 finally 語句塊是確定要執行的,其中也包括一些頗有經驗的 Java 程序員。惋惜並不像大多人所認爲的那樣,對於這個問題,答案固然是否認的,咱們先來看下面這個例子。程序員
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of test(): " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 // if(i == 1) 9 // return 0; 10 System.out.println("the previous statement of try block"); 11 i = i / 0; 12 13 try { 14 System.out.println("try block"); 15 16 return i; 17 } finally { 18 System.out.println("finally block"); 19 } 20 } 21 22 } 23
清單 1 的執行結果以下:函數
the previous statement of try block Exception in thread "main" java.lang.ArithmeticException: / by zero at com.bj.charlie.Test.test(Test.java:15) at com.bj.charlie.Test.main(Test.java:6) |
另外,若是去掉上例中被註釋的兩條語句前的註釋符,執行結果則是:spa
return value of test(): 0 |
在以上兩種狀況下,finally 語句塊都沒有執行,說明什麼問題呢?
只有與 finally 相對應的 try 語句塊獲得執行的狀況下,finally 語句塊纔會執行。
以上兩種狀況,都是在 try 語句塊以前返回(return)或者拋出異常,因此 try 對應的 finally 語句塊沒有執行。線程
那好,即便與 finally 相對應的 try 語句塊獲得執行的狀況下,finally 語句塊必定會執行嗎?
請看下面這個例子(清單 2)。code
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of test(): " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 9 try { 10 System.out.println("try block"); 11 System.exit(0); 12 13 return i; 14 } finally { 15 System.out.println("finally block"); 16 } 17 } 18 }
清單 2 的執行結果以下:ci
try block |
finally 語句塊仍是沒有執行,爲何呢?
由於咱們在 try 語句塊中執行了 System.exit (0) 語句,終止了 Java 虛擬機的運行。
那有人說了,在通常的 Java 應用中基本上是不會調用這個 System.exit(0) 方法的。
OK !沒有問題,咱們不調用 System.exit(0) 這個方法,那麼 finally 語句塊就必定會執行嗎?get
答案仍是否認的。
當一個線程在執行 try 語句塊或者 catch 語句塊時被打斷(interrupted)或者被終止(killed),與其相對應的 finally 語句塊可能不會執行。
還有更極端的狀況,就是在線程運行 try 語句塊或者 catch 語句塊時,忽然死機或者斷電,finally 語句塊確定不會執行了。
可能有人認爲死機、斷電這些理由有些強詞奪理,沒有關係,咱們只是爲了說明這個問題。
下面,咱們先來看一個簡單的例子(清單 3)。
1 public class Test { 2 public static void main(String[] args) { 3 try { 4 System.out.println("try block"); 5 6 return; 7 } finally { 8 System.out.println("finally block"); 9 } 10 } 11 }
清單 3 的執行結果爲:
try block finally block |
清單 3 說明 finally 語句塊在 try 語句塊中的 return 語句以前執行
咱們再來看另外一個例子
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("reture value of test() : " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 9 try { 10 System.out.println("try block"); 11 i = 1 / 0; 12 13 return 1; 14 } catch (Exception e) { 15 System.out.println("exception block"); 16 17 return 2; 18 } finally { 19 System.out.println("finally block"); 20 } 21 } 22 }
清單 4 的執行結果爲:
try block exception block finally block reture value of test() : 2 |
清單 4 說明了 finally 語句塊在 catch 語句塊中的 return 語句以前執行。
從上面的清單 3 和清單 4,咱們能夠看出,
其實 finally 語句塊是在 try 或者 catch 中的 return 語句以前執行的。
更加通常的說法是,finally 語句塊應該是在控制轉移語句以前執行,控制轉移語句除了 return 外,還有 break 和 continue。
另外,throw 語句也屬於控制轉移語句。雖然 return、throw、break 和 continue 都是控制轉移語句,可是它們之間是有區別的。
其中 return 和 throw 把程序控制權轉交給它們的調用者(invoker),而 break 和 continue 的控制權是在當前方法內轉移。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 try { 8 return 0; 9 } finally { 10 return 1; 11 } 12 } 13 }
清單 5 的執行結果:
return value of getValue(): 1 |
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 int i = 1; 8 9 try { 10 return i; 11 } finally { 12 i++; 13 } 14 } 15 } 16
清單 6 的執行結果:
return value of getValue(): 1 |
利用咱們上面分析得出的結論:
finally 語句塊是在 try 或者 catch 中的 return 語句以前執行的。
由此,能夠輕鬆的理解清單 5 的執行結果是 1。
由於 finally 中的 return 1;語句要在 try 中的 return 0;語句以前執行,那麼 finally 中的 return 1;
語句執行後,把程序的控制權轉交給了它的調用者 main()函數,而且返回值爲 1。
那爲何清單 6 的返回值不是 2,而是 1 呢?
按照清單 5 的分析邏輯,finally 中的 i++;
語句應該在 try 中的 return i;以前執行啊?
i 的初始值爲 1,那麼執行 i++;以後爲 2,再執行 return i;那不就應該是 2 嗎?怎麼變成 1 了呢?
咱們來分析一下其執行順序:分爲正常執行(沒有 exception)和異常執行(有 exception)兩種狀況。咱們先來看一下正常執行的狀況,如圖 1 所示:
由上圖,咱們能夠清晰的看出,在 finally 語句塊(iinc 0, 1)執行以前,getValue()方法保存了其返回值(1)到本地表量表中 1 的位置,完成這個任務的指令是 istore_1;而後執行 finally 語句塊(iinc 0, 1),finally 語句塊把位於 0 這個位置的本地變量表中的值加 1,變成 2;待 finally 語句塊執行完畢以後,把本地表量表中 1 的位置上值恢復到操做數棧(iload_1),最後執行 ireturn 指令把當前操做數棧中的值(1)返回給其調用者(main)。這就是爲何清單 6 的執行結果是 1,而不是 2 的緣由。
再讓咱們來看看異常執行的狀況。是否是有人會問,你的清單 6 中都沒有 catch 語句,哪來的異常處理呢?我以爲這是一個好問題,其實,即便沒有 catch 語句,Java 編譯器編譯出的字節碼中仍是有默認的異常處理的,別忘了,除了須要捕獲的異常,還可能有不需捕獲的異常(如:RunTimeException 和 Error)。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 @SuppressWarnings("finally") 7 public static int getValue() { 8 int i = 1; 9 10 try { 11 i = 4; 12 } finally { 13 i++; 14 15 return i; 16 } 17 } 18 }
清單 7 的執行結果:
return value of getValue(): 5 |
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 int i = 1; 8 9 try { 10 i = 4; 11 } finally { 12 i++; 13 } 14 15 return i; 16 } 17 }
清單 8 的執行結果:
return value of getValue(): 5 |
讓咱們再來看一個稍微複雜一點的例子 – 清單 9。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println(test()); 4 } 5 6 public static String test() { 7 try { 8 System.out.println("try block"); 9 10 return test1(); 11 } finally { 12 System.out.println("finally block"); 13 } 14 } 15 16 public static String test1() { 17 System.out.println("return statement"); 18 19 return "after return"; 20 } 21 }
清單 9 的結果:
try block return statement finally block after return |
return test1();這條語句等同於 :
String tmp = test1(); return tmp; |
這樣,就應該清楚爲何是上面所示的執行結果了吧!