關於java中 try和finally return的問題

 關於java中finally,以前只是用在try catch 之後,今天同學發來一個面試題給我詳細一分析,立刻覺得java中的這部分設計有點繁瑣。代碼如下:
  1. public int test()  
  2. {  
  3.      int a = 1;  
  4.      try  
  5.      {  
  6.           return a;  
  7.      }catch  
  8.      {   
  9.           //todo:handle exception  
  10.      }finally  
  11.      {  
  12.           a++;  
  13.      }  
  14.      return a;  
  15. }
    要得出return a 爲多少,是不是有點讓人迷糊。
表現
    1.不是太明白try finally執行順序的人做,很快就能得出答案,程序會執行到try代碼塊裏面,直接返回a=1.這是正確答案,要講出詳細執行過程可能會講不出來。
    2.明白try finally執行順序的人做,可能會得出 a=2的答案,這是錯誤答案。
 分析
    知道try catch finally執行順序的人都知道,上面的程序塊中,首先會執行try中的代碼,在執行try中的return前,程序會去執行finally中的a++,執行完finally中的a++ 然後再去執行try中的return。
    那麼爲什麼執行了finally中的a++ 後,try中的return返回的仍然是a=1呢。這需要深入分析。
深入分析
   一些書上寫明,1.try中return的是基本類型的數據,那麼finally中的改動不會反映到try中。2.try中return的是引用,那麼finally中對相同引用的改動會反映到try中。
    爲什麼會有上面兩個結論,得從調用函數、參數棧的角度來解釋。當調用一個函數時,將函數所需的參數依次壓棧,函數裏面直接取用這些參數,在函數返時將返回值壓棧,函數返回後,棧頂即是返回值。當通過壓棧傳遞參數時,參數的類型不同,壓棧的內容也不同。如果是值類型,壓棧就是經過複製的參數值,如果是引用類型,那麼進棧的只是一個引用。這也是我們所熟悉的,傳遞值類型時,函數內修改參數值不會影響函數外,而引用類型的話則會影響。
通過上面的分析之後,那麼下面的代碼就很好得出答案:

public class JVMTest {  

public static void main(String[] args){  

System.out.println("aa:" + aa());  

}  

public static int aa(){  

int a = 1;  

int b = 10;  

try{  

System.out.println("abc");  

return a;  

}finally{  

a = 2;  

System.out.println("a: "+ a);  

}  

}  

}

abc

a: 2

aa: 1

    上面的輸出結果很好分析,首先執行try中的println,然後執行return,在return前,去執行finally中的println,執行完finally中的代碼後再去try中return,然後輸出main函數中的結果。
-----------------------------------------------------割裂 -----------------------------------------------------------------
    上面的分析是最常規的情況,下面分析一下不常規的情況,雖然實際中可能很少能見到,但是對於理解java中finally的設計有相當大的幫助。 
    如果上面的代碼進行變形,如下:

public class FinallyTest { public static void main(String[] args) { System.out.println("getValue()返回值爲:" + getValue()); } public static int getValue() { try { return 0; } finally { return 1; } } }

那麼,此時的輸出結果是什麼呢?
按照上面的分析:在try中return前,執行finally中的代碼,return 1,然後return 0,所以最終的返回值爲0.
這樣的分析正確嗎,總讓人有種怪怪的感覺,那麼執行過程又是怎樣的呢?(正確答案是返回 1)
讓java之父James Gosling來解釋:

a finally clause is always entered with a reason. That reason may be that the trycode finished normally, that it executed a control flow statement such as return, or that an exception was thrown in code executed in the TRy block. The reason is remembered when the finally clause exits by falling out the bottom. However, ifthe finally block creates its own reason to leave by executing a control flow statement (such as break or return) or by throwing an exception, that reason supersedes the original one, and the original reason is forgotten. For example, consider the following code:

try {

    // ... do something ...

    return 1;

} finally {

    return 2;

}

When the TRy block executes its return, the finally block is entered with the "reason" of returning the value 1. However, inside the finally block the value 2 is returned, so the initial intention is forgotten. In fact, if any of the other code in the try block had thrown an exception, the result would still be to return 2. If the finally block did not return a value but simply fell out the bottom, the "return the value 1" reason would be remembered and carried out.

    上述紅色關鍵字體的意思是,當try正常結束(code finished normally)包括return, or that an exception,程序就會跳轉到finally塊中去,如果finally中沒有such as break or return or by throwing an exception這些發生,try中的return就會正常返回;如果finally中有break 或者 return 或者拋出異常,那麼try中的return值就會被清除,按照finally中的結果來返回。

    按照上面的描述,是不是對finally的用法更加了解了呢。

    爲什麼會出現這種分情況的討論,究其原因,都是java的設計規則引起的。結合上面的三段代碼及運行過程分析,可以分析任何try 和finally的返回值問題了。

發佈了28 篇原創文章 · 獲贊 9 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章