2015-09-08 49 views
2

鑑於Main.java:麻煩試圖掌握產業

public class Main{ 
    public static void main(String[]args){ 
      A a = new B(); 
      a.print(); 
    } 

} 

class A{ 
     A() {print();} 
     void print() { System.out.println("A"); } 
} 

class B extends A{ 
     int i = 4; 
     void print() { System.out.println(i); } 
} 

結果:
4.
但爲什麼不a.print輸出 「A」,如果它引用類?在這種情況下,我怎麼知道一種方法會被稱爲另一種方法呢?爲什麼A的構造函數被調用並仍然使用B的方法?

+0

'a.print'不引用類A,a只引用類型'B'的類型'A'的對象。 – Geinmachi

+0

這是爲什麼我們應該避免構造函數中的多態方法的很好的例子。爲了避免這些問題,只能使用私有,靜態或最終的方法。 – Pshemo

回答

5

致電a.print()打印4因爲polymorphism。所調用的方法取決於a的運行時間類型,即B。它叫什麼時候並不重要;多態總是適用的。

兩次,Bprint方法被調用。一次來自A的構造函數,它由B中的默認構造函數調用。另一次是您在main的明確電話。

第一次打印得到0而不是4的原因是因爲在調用print時,A仍在構建中。也就是說,A構造函數仍在執行中。在超類構造函數返回之前,子類中沒有任何東西被初始化,甚至沒有變量初始值設定項。在超類構造函數完成後,但在子類構造函數的其餘部分完成之前,值4被分配。因爲變量初始值設定項尚未運行,所以缺省值0(對於對象booleannull,將爲false)爲第一次打印時的值i

此順序由列出的JLS, Section 12.5

就在到新創建的對象的引用被作爲結果返回時,所指示的構造被處理使用下面的過程來初始化新的對象:

  1. 將構造函數的參數分配給此構造函數調用的新創建的參數變量。

  2. 如果此構造函數以相同類中另一個構造函數的顯式構造函數調用(第8.8.7.1節)開頭(使用此參數),則計算參數並使用這五個相同的步驟來遞歸構造函數調用。如果構造函數的調用突然完成,則此過程由於同樣的原因突然完成;否則,請繼續執行步驟5.

  3. 此構造函數不是以相同類中另一個構造函數的顯式構造函數調用開始的(使用此方法)。如果此構造函數用於Object以外的類,則此構造函數將以顯式或隱式調用超類構造函數(使用super)開始。評估參數並使用這五個相同的步驟遞歸地處理該超類構造函數的調用。如果構造函數的調用突然完成,那麼出於同樣的原因,此過程會突然完成。否則,請繼續步驟4。

  4. 執行實例初始化和實例變量初始化這個類,分配實例變量初始化的值,以相應的實例變量,在左到右的順序在它們文本地出現在源代碼班上。如果執行這些初始化程序中的任何一個都會導致異常,則不會執行進一步的初始化程序,並且此過程突然以相同的異常完成。否則,請繼續步驟5.

  5. 執行此構造函數的其餘部分。如果該執行突然完成,則此過程因相同原因突然完成。否則,此過程正常完成。

(粗體重點煤礦)

這就是爲什麼它是一個壞主意,打電話可以從構造函數中覆蓋的方法的例子。子類狀態尚未初始化。

1

在覆蓋方法的情況下(如打印()在你的例子),對象類型決定要調用哪些方法,而不是參考類型。

0

B沒有任何構造函數,所以它的默認構造函數除了調用A的構造函數外不會做任何事情。

現在當B的默認構造函數被調用時,它調用A的構造函數(記住i還沒有設置,所以默認值爲0)。 A的構造函數調用print(),現在對象實際上是B調用B的print(),它打印0(記得i未設置)。

現在,一旦這些構造函數調用完成B的代碼得到執行,其將i爲0。現在調用print()會再次讓你到B的打印()(作爲對象僅是B的),這將打印4

「調試是關鍵」