2009-10-28 39 views
2

傳遞最終對象(下面代碼中的字符串)時,它從匿名內部類打印時顯示爲空。但是,當最終值類型或直接最終字符串傳入時,其值正確顯示。 final在匿名內部類的上下文中是什麼意思,爲什麼對象傳遞null?傳入匿名內部類時的空對象

public class WeirdInners 
{ 
    public class InnerThing 
    { 
     public InnerThing() 
     { 
      print(); 
     } 

     public void print(){ 

     } 
    } 

    public WeirdInners() 
    { 
     final String aString = "argh!".toString(); 
     final String bString = "argh!"; 
     System.out.println(aString); 
     System.out.println(bString); 


     InnerThing inner =new InnerThing(){ 
      public void print() 
      { 
       System.out.println("inner"+aString); // When called from constructor, this value is null. 
       System.out.println("inner"+bString); // This value is correctly printed. 
      } 
     }; 

     inner.print(); 
    } 


    public static void main(String[] args) 
    { 
     WeirdInners test1 = new WeirdInners(); 
    } 

} 

這是非常奇怪的行爲,我,因爲期望是字符串對象,爲什麼叫toString()變化的東西呢?

其他信息:此行爲僅在Java 1.4中觀察到,而不在Java 5中。有關解決方法的任何建議?在現有字符串上不調用toString()是公平的,但由於這只是一個例子,如果我在非String對象上執行它,它會有真實世界的影響。

+0

你在Java 1.4下看到了什麼結果?我正在使用Java 6,並且我得到了(可能不會很好地格式化,並且沒有預覽註釋): argh! 啊! innerargh! innerargh! innerargh! innerargh! 這不是對你有任何幫助,但EOL Notification for Java 1.4於2006年12月宣佈,EOSL for Java 1.4是本週五一年前發佈的。Java 5的EOL Notification是在2008年4月,它在本週五觸及了EOSL。任何你可以升級到Java 6的機會? http://java.sun.com/products/archive/eol.policy.html – shoover 2009-10-28 21:09:37

+0

您將看到預期的輸出。使用「argh!」時1.4行爲是innernull。toString();. – 2009-10-28 23:11:52

回答

5

如果您在JLS中查看compile-time constants的部分,您會發現致電.toString()確實有所作爲。像廢話一樣前綴false?null+"":

這裏重要的是設置關閉的字段和構造函數的相對順序。如果您使用-target 1.4或更高版本(這不是1.4中的默認值),那麼在調用super之前,這些字段將被複制。在1.3之前的規範中,這是非法的字節碼。

在這些情況下通常會出現這種情況,javap -c對於瞭解javac編譯器在做什麼很有用。該規範對理解爲什麼有用(應該有足夠的耐心)。

+1

+1。你必須在你的牀頭櫃上有JLS :-) – ChssPly76 2009-10-28 21:26:21

0

我的猜測是,當InnerThing()的構造函數將其this傳遞給InnerThing的匿名子類的打印方法,而對象未完全構造時,則會觸發未定義的行爲。這個this反過來依賴於對WierdInners的this的隱式引用。

調用.toString()將aString從編譯時初始化爲運行時。爲什麼未定義的行爲在Java 1.4和1.5之間有所不同,這可能是JVM實現細節。

0

從超類構造函數中調用重寫的方法是很危險的,因爲它們將在對象的子類部分初始化之前調用。此外,訪問封閉範圍的最終變量的內部類將實際訪問這些變量的副本(這就是爲什麼它們需要是最終的),並且這些複製的字段駐留在匿名子類中。

我懷疑bString的處理方式不同是因爲它的值在編譯時已知,這允許編譯器內聯子類中的字段訪問,使得字段初始化的時間無關緊要。