2012-06-25 37 views
14
public class Test2 { 

    public static void main(String[] args) { 
     Test2 obj=new Test2(); 
     String a=obj.go(); 

     System.out.print(a); 
    } 


    public String go() { 
     String q="hii"; 
     try { 
      return q; 
     } 
     finally { 
      q="hello"; 
      System.out.println("finally value of q is "+q); 
     } 
    } 

爲什麼打印hii從函數go()返回後,值在最後一塊變成了「hello」?奇怪的終於行爲?

程序的輸出是

finally value of q is hello 
hii 
+1

您可能想閱讀:http://stackoverflow.com/questions/65035/in-java-does-return-trump-finally –

+2

局部變量'q'的值已更改爲「hello」,是。但你回來的是「hii」。 – maksimov

+0

請看我的新答案...讓我們嘗試更加細化這個概念.. – Ahmad

回答

27

那是因爲你返回了從q您在finally塊改變q前值評估的值。你回來了q,它評估了它的價值;那麼您在finally塊中更改了q,這並未影響加載的值;然後返回完成,使用評估值。

不要寫這樣的棘手的代碼。如果它讓寫這篇文章的人感到困惑,那麼想象一下,當你在別的地方的時候,它會給下一個人帶來什麼問題,幾年下來。

+0

但是在finally塊執行後返回值..所以 – Harinder

+1

@Dennis返回的值在finally塊執行之前加載。我已經說過了。 – EJP

+1

@丹尼斯是的,但在'終於'你已經_past_'返回'指令,不是在它之前。 – maksimov

0

[在EJP評論後編輯,我的第一個迴應沒有回答問題,也是錯的。]
現在我的答案應該是正確的解釋,因爲try塊和finally塊正常完成q返回。 EJP答案解釋了返回值「hii」的原因。我仍然在尋找JLS的解釋。

有finally塊在try語句時,首先執行try塊執行一看JLS 14.20.2 Execution of try-catch-finally

。然後有一個選擇:

如果try塊的執行正常完成,那麼將執行finally塊,然後有一個選擇:
如果finally塊正常完成,則try語句正常完成。
[...]

JLS 14.17 The return Statement

帶表達式的返回語句試圖將控制轉移到包含它的方法的調用; Expression的值將成爲方法調用的值。更準確地說,執行這樣的返回語句首先評估表達式。如果由於某種原因,表達式的評估突然完成,那麼由於這個原因,返回語句會突然完成。如果表達式的計算正常完成,產生一個值V,則返回語句完成突然,原因是與值V返回

和:

前面的描述說:「試圖轉移控制「而不僅僅是」傳輸控制「,因爲如果在try塊包含返回語句的方法或構造函數中有任何try語句(第14.20節),那麼這些try語句的任何finally子句將按順序執行,最內層在控制轉移到方法或構造函數的調用者之前的最外層。突然完成finally子句可能會中斷由return語句啓動的控制權轉移。

+1

這究竟如何回答這個問題? – EJP

+0

仍然不回答這個問題。 'q'不*返回。返回語句執行時*的q的*值。 – EJP

4

return返回不參考。當return q;得到執行catch當前值q引用被緩存方法作爲其結果。因此,即使在finally區塊中您將重新分配q新值,但它不會影響已由方法緩存的值。

如果要更新應當返還你將不得不使用其他returnfinally塊像

} finally { 
    q = "hello"; 
    System.out.println("finally value of q is " + q); 

    return q;//here you set other value in return 
} 

影響返回值的其他方式值是通過改變狀態緩存對象。例如,如果qList,我們可以添加新元素(但注意,狀態更改與重新分配新實例不同,就像我們可以更改變量狀態final變量一樣,但我們不能重新分配它)。

} finally { 
    q.add(new Element); //this will place new element (update) in List 
    //object stored by return because it is same object from q reference 
    System.out.println("finally value of q is " + q); 
} 
+0

'來自+引用的值(對象)+,不是精確的引用','從引用讀取/存儲對象'以及'返回存儲的對象'是毫無意義的。沒有答案。這裏可能有一個答案努力擺脫,但術語是如此混亂,沒有意義傳達。 – EJP

2

最後在返回後但在方法實際返回給調用者之前執行。這類似於扔。它發生在拋出之後和退出塊之前。返回值已經通過讀取變量q在某個寄存器中設置。如果q是可變的,你可以最終改變它,你會看到調用者的變化。它爲什麼這樣工作?首先,它可能是最不復雜的實施。二,它給你最大的靈活性。您可以通過顯式返回來覆蓋返回值。默認情況下保留它可讓您選擇任一行爲。

0

嘗試使用StringBuffer而不是String並且您將看到更改....看起來返回語句阻止要返回的對象而不是參考。您也可以嘗試通過打印的哈希碼進行驗證:

  • 對象從去()
  • 對象返回終於
  • 對象正在從主()

    公共靜態無效印刷主要(字串[] args){

    Test obj=new Test(); 
         StringBuffer a=obj.go(); 
         System.out.print(a); 
        } 
        public StringBuffer go() { 
         StringBuffer q=new StringBuffer("hii"); 
         try { 
          return q; 
         } 
         finally { 
          q=q.append("hello"); 
          System.out.println("finally value of q is "+q); 
         } 
        } 
    
+0

'阻止要返回的對象'是沒有意義的。沒有答案。 – EJP

-1

好吧,我發現了什麼是如下所示:

返回實際返回值並將其複製到String a=obj.go();,然後執行到最後

讓我們通過以下實驗進行驗證。

public class Test2 { 

    public static void main(String[] args) { 
    Test2 obj=new Test2(); 
    String a=obj.go(); 

    System.out.print(a); 
    } 


    public String go() { 
    String q="hii"; 
    try { 
     return q; 
    } 
    finally { 
     q="hello"; 
     System.out.println("finally value of q is "+q); 
    } 
} 

程序的輸出爲

最後的Q值是你好

HII

,如果我們採取的,而不是字符串的StringBuffer如下,

public class Test2 { 

    public static void main(String[] args) { 
     // TODO Auto-generated method stub 

     Test2 obj=new Test2(); 
     StringBuffer a=obj.go(); 

     System.out.print(a); 
    } 


    public StringBuffer go(){ 
     StringBuffer q=new StringBuffer("hii"); 
     try{ 

      return q; 
     } 
     finally{ 

      q.replace(0, q.length(), "hello"); 
      System.out.println("finally value of q is "+q); 
      /*return q1;*/ 

     } 

    } 
} 

輸出comesout是,

最後Q的值是你好

你好

,最後,如果我們採取的int,而不是字符串如下,

public class Test2 { 

    public static void main(String[] args) { 
     // TODO Auto-generated method stub 

     Test2 obj=new Test2(); 
     int a=obj.go(); 

     System.out.print(a); 
    } 


    public int go(){ 
     int q=1; 
     try{ 

      return q; 
     } 
     finally{ 

      q=2; 
      System.out.println("finally value of q is "+q); 
      /*return q1;*/ 

     } 

    } 
} 

輸出是

最後Q的值是2

       **Ananlysis** 

1.In第一種情況下,返回字符串的複製ADRESS在可變一個,然後excecution去最後在哪裏字符串被改變。但是,既然在字符串的情況下,我們不能操縱任何字符串一個新的字符串構造。所以在變量一個原始字符串的地址被保存,它被打印。

2.在第二種情況下,返回變量a中的StringBuffer的複製地址,最後對該StringBuffer對象進行操作,而不是創建新的地址。所以存儲在變量a中的值也被操縱,這在打印語句中可見。

3.在第三種情況下,將int的值複製到變量a中,然後執行到最後。因此a得到1的值,然後在最後我們改變了值q,其仍然不改變值a

+0

很正確..沒有看到你的答案... – Ahmad

+0

你的前兩個例子都是不相關的。第一個是簡單的錯誤代碼,因爲String.repace()不會改變該值,它會返回一個新的值,並將其丟棄。第二個確實改變了價值,但因爲這不是OP所做的事情,所以我沒有看清這一點。第三個只是重申OP在詢問什麼,並重復已經給出的答案。 – EJP

0

什麼是終止塊?

-By definition from Java「finally塊總是在try塊退出時執行,這確保即使發生意外的異常也會執行finally塊。

因此,只要存在try塊並轉到System.out.print(a)行,就會打印出「q的值是hello」。並打印方法go()返回的值。

如果你有像NetBeans或eclipse這樣的調試器,可以通過保持斷點並通過代碼喚醒來分析它。