可不能小看這個簡單的 finally,看似簡單的問題背后,卻隱藏了無數的玄機。接下來我就帶您一步一步的揭開這個 finally 的神秘面紗。
問題分析
首先來問大家一個問題:finally 語句塊一定會執(zhí)行嗎?
很多人都認為 finally 語句塊是肯定要執(zhí)行的,其中也包括一些很有經驗的 Java 程序員。可惜并不像大多人所認為的那樣,對于這個問題,答案當然是否定的,我們先來看下面這個例子。
清單 1.
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"); } } }
清單 1 的執(zhí)行結果如下:
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)
另外,如果去掉上例中被注釋的兩條語句前的注釋符,執(zhí)行結果則是:
return value of test(): 0
在以上兩種情況下,finally 語句塊都沒有執(zhí)行,說明什么問題呢?只有與 finally 相對應的 try 語句塊得到執(zhí)行的情況下,finally 語句塊才會執(zhí)行。以上兩種情況,都是在 try 語句塊之前返回(return)或者拋出異常,所以 try 對應的 finally 語句塊沒有執(zhí)行。
那好,即使與 finally 相對應的 try 語句塊得到執(zhí)行的情況下,finally 語句塊一定會執(zhí)行嗎?不好意思,這次可能又讓大家失望了,答案仍然是否定的。請看下面這個例子(清單 2)。
清單 2.
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"); } } }
清單 2 的執(zhí)行結果如下:
try block
finally 語句塊還是沒有執(zhí)行,為什么呢?因為我們在 try 語句塊中執(zhí)行了 System.exit (0) 語句,終止了 Java 虛擬機的運行。那有人說了,在一般的 Java 應用中基本上是不會調用這個 System.exit(0) 方法的。OK !沒有問題,我們不調用 System.exit(0) 這個方法,那么 finally 語句塊就一定會執(zhí)行嗎?
再一次讓大家失望了,答案還是否定的。當一個線程在執(zhí)行 try 語句塊或者 catch 語句塊時被打斷(interrupted)或者被終止(killed),與其相對應的 finally 語句塊可能不會執(zhí)行。還有更極端的情況,就是在線程運行 try 語句塊或者 catch 語句塊時,突然死機或者斷電,finally 語句塊肯定不會執(zhí)行了??赡苡腥苏J為死機、斷電這些理由有些強詞奪理,沒有關系,我們只是為了說明這個問題。
finally 語句剖析
說了這么多,還是讓我們拿出些有說服力的證據吧!還有什么證據比官方的文檔更具說服力呢?讓我們來看看官方網站上的《The Java Tutorials》中是怎樣來描述 finally 語句塊的吧!
以下位于 **** 之間的內容原封不動的摘自于《 The Java Tutorials 》文檔。
*******************************************************************************
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.
*******************************************************************************
請仔細閱讀并認真體會一下以上兩段英文,當你真正的理解了這兩段英文的確切含義,你就可以非常自信的來回答“finally 語句塊是否一定會執(zhí)行?”這樣的問題??磥?,大多時候,并不是 Java 語言本身有多么高深,而是我們忽略了對基礎知識的深入理解。
接下來,我們看一下 finally 語句塊是怎樣執(zhí)行的。在排除了以上 finally 語句塊不執(zhí)行的情況后,finally 語句塊就得保證要執(zhí)行,既然 finally 語句塊一定要執(zhí)行,那么它和 try 語句塊與 catch 語句塊的執(zhí)行順序又是怎樣的呢?還有,如果 try 語句塊中有 return 語句,那么 finally 語句塊是在 return 之前執(zhí)行,還是在 return 之后執(zhí)行呢?帶著這樣一些問題,我們還是以具體的案例來講解。
關于 try、catch、finally 的執(zhí)行順序問題,我們還是來看看權威的論述吧!以下 **** 之間的內容摘自 Java 語言規(guī)范第四版(《The Java™ Programming Language, Fourth Edition》)中對于 try,catch,和 finally 的描述。
*******************************************************************************
12.4. Try, catch, and finally
You catch exceptions by enclosing code in Try blocks. The basic syntax for a Try block is:try {statements} catch (exception_type1 identifier1) {statements} catch (exception_type2 identifier2) {statements...} finally {statements}
where either at least one catch clause, or the finally clause, must be present. The body of the try statement is executed until either an exception is thrown or the body finishes successfully. If an exception is thrown, each catch clause is examined in turn, from first to last, to see whether the type of the exception object is assignable to the type declared in the catch. When an assignable catch clause is found, its block is executed with its identifier set to reference the exception object. No other catch clause will be executed. Any number of catch clauses, including zero, can be associated with a particular TRy as long as each clause catches a different type of exception. If no appropriate catch is found, the exception percolates out of the try statement into any outer try that might have a catch clause to handle it.
If a finally clause is present with a try, its code is executed after all other processing in the try is complete. This happens no matter how completion was achieved, whether normally, through an exception, or through a control flow statement such as return or break.
*******************************************************************************
上面這段文字的大體意思是說,不管 try 語句塊正常結束還是異常結束,finally 語句塊是保證要執(zhí)行的。如果 try 語句塊正常結束,那么在 try 語句塊中的語句都執(zhí)行完之后,再執(zhí)行 finally 語句塊。如果 try 中有控制轉移語句(return、break、continue)呢?那 finally 語句塊是在控制轉移語句之前執(zhí)行,還是之后執(zhí)行呢?似乎從上面的描述中我們還看不出任何端倪,不要著急,后面的講解中我們會分析這個問題。如果 try 語句塊異常結束,應該先去相應的 catch 塊做異常處理,然后執(zhí)行 finally 語句塊。同樣的問題,如果 catch 語句塊中包含控制轉移語句呢? finally 語句塊是在這些控制轉移語句之前,還是之后執(zhí)行呢?我們也會在后續(xù)討論中提到。
其實,關于 try,catch,finally 的執(zhí)行流程遠非這么簡單,有興趣的讀者可以參考 Java 語言規(guī)范第三版(《The Java™ Language Specification, Third Edition》)中對于 Execution of try-catch-finally 的描述,非常復雜的一個流程。限于篇幅的原因,本文不做摘錄,請感興趣的讀者自行閱讀。
finally 語句示例說明
下面,我們先來看一個簡單的例子(清單 3)。
清單 3.
public class Test { public static void main(String[] args) { try { System.out.println("try block"); return ; } finally { System.out.println("finally block"); } } }
清單 3 的執(zhí)行結果為:
try block finally block
清單 3 說明 finally 語句塊在 try 語句塊中的 return 語句之前執(zhí)行。我們再來看另一個例子(清單 4)。
清單 4.
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"); } } }
清單 4 的執(zhí)行結果為:
try block exception block finally block reture value of test() : 2
清單 4 說明了 finally 語句塊在 catch 語句塊中的 return 語句之前執(zhí)行。
從上面的清單 3 和清單 4,我們可以看出,其實 finally 語句塊是在 try 或者 catch 中的 return 語句之前執(zhí)行的。更加一般的說法是,finally 語句塊應該是在控制轉移語句之前執(zhí)行,控制轉移語句除了 return 外,還有 break 和 continue。另外,throw 語句也屬于控制轉移語句。雖然 return、throw、break 和 continue 都是控制轉移語句,但是它們之間是有區(qū)別的。其中 return 和 throw 把程序控制權轉交給它們的調用者(invoker),而 break 和 continue 的控制權是在當前方法內轉移。請大家先記住它們的區(qū)別,在后續(xù)的分析中我們還會談到。
還是得來點有說服力的證據,下面這段摘自 Java 語言規(guī)范第四版(《The Java™ Programming Language, Fourth Edition》),請讀者自己體會一下其含義。
*******************************************************************************
Afinallyclause can also be used to clean up forbreak,continue, andreturn, which is one reason you will sometimes see atryclause with nocatchclauses. When any control transfer statement is executed, all relevantfinallyclauses are executed. There is no way to leave atryblock without executing itsfinallyclause.
*******************************************************************************
好了,看到這里,是不是有人認為自己已經掌握了 finally 的用法了?先別忙著下結論,我們再來看兩個例子
主站蜘蛛池模板:
金秀|
慈利县|
古浪县|
石柱|
敖汉旗|
马鞍山市|
乌拉特中旗|
麟游县|
开阳县|
前郭尔|
长沙市|
本溪|
河西区|
南郑县|
剑阁县|
加查县|
山阳县|
韶关市|
丰县|
红原县|
柏乡县|
平南县|
安达市|
昌都县|
奉新县|
团风县|
苗栗县|
临海市|
通河县|
石城县|
大关县|
合山市|
吉安市|
长白|
武宁县|
东光县|
台山市|
江永县|
白银市|
洛浦县|
通辽市|