2016-01-15 89 views
1

我偶然發現了這段代碼。
我試圖猜測在實際運行之前運行它的結果是什麼。 當我看到他們&需要一些解釋時,我真的很困惑。
這是代碼:使用繼承和重寫方法在Java中複雜輸出

public class A { 
    String bar = "A.bar"; 
    A() { foo(); } 

    public void foo() { 
     System.out.println("A.foo(): bar = " + bar); 
    } 
} 

public class B extends A { 
    String bar = "B.bar"; 
    B() { foo(); } 
    public void foo() { 
     System.out.println("B.foo(): bar = " + bar); 
    } 
} 

public class C { 
    public static void main(String[] args) { 
     A a = new B(); 
     System.out.println("a.bar = " + a.bar); 
     a.foo(); 
    } 
} 

輸出是:

B.foo(): bar = null 
B.foo(): bar = B.bar 
a.bar = A.bar 
B.foo(): bar = B.bar 

這是爲什麼?

  • bar = null怎麼樣?
  • 爲什麼a.bar = A.bar甚至出現?我沒有實例化A
  • 如果A出現,爲什麼它B

回答

5

有幾個事實,你應該知道開始之前,我解釋你的代碼的執行每一個步驟:

  • 字段引用是基於引用類型和方法調用解決了在運行時進行解析(以動態的方式)基於對象類型。
  • super()放置隱含構造,即使你不把它存在自己(如果你調用super(int x, int y)例如它不叫)。
  • 從構造函數中調用「override-able」方法被認爲是非常糟糕的做法 - 當我們完成執行時,您會明白爲什麼。

現在,讓我們打破你的代碼一步一步:

  • 您可以通過調用它的默認構造函數B()實例B
  • 正如我之前所說的,super()的調用是隱含添加到任何構造函數,所以A()立即被調用。
  • Inside A()您可以撥打電話foo(),這在類別B中被覆蓋,這就是爲什麼foo()是從B中調用的原因。
  • Bfoo()你得到的輸出B.foo(): bar = null自從Java沒有得到初始化B的領域,但(其構造尚未執行!)和對象類型的字段默認初始化爲null。我們回到構造函數B()
  • 在上述構造函數中,我們再次調用foo(),這又是Bfoo()。但與上次不同的是,B已將其字段初始化(在致電super()之後),因此您可以獲得預期的B.foo(): bar = B.bar
  • 現在我們回到main的溫暖懷抱。
  • 您訪問a.bar,因爲正如我所說的現場參考是基於參考類型解析,您可以得到的字段bar
  • 最後,出於同樣的原因,您致電a.foo(),再次觸發Bfoo(),再次打印b.bar

我們完成了! :)

更多參考和有價值的閱讀材料:
Static and dynamic binding explained
Order of constructor calls