2015-10-19 82 views
3

考慮下面的代碼塊我想知道爲什麼和怎麼樣,強調如何,在子類中的方法打印被調用:調用基類重寫的方法從父類的構造函數

class Super { 

    Super() { 

     // what happens so that Sub's method print() is invoked 
     print(); 
    } 

    public void print() { 

     System.out.println("in super"); 
    } 
} 

class Sub extends Super { 

    Sub() { 

     super(); 
    } 

    public void print() { 

     System.out.println("in sub"); 
    } 
} 

public class TestClass { 

    public static void main(String[] args) { 


     Super s = new Sub(); // "in sub".. not so much expected 

     s.print(); // "in sub".. as expected 
    } 
} 

我理解是在編譯期間,類將獲得與方法'屬於'類相關聯的V表指針vtblPtr。 類Super因此應參考它自己的方法print()的實現。

Super構造方法中如何調用Sub中的方法print()?這裏真的發生了什麼?

+0

我認爲你很困惑C++和Java:在Java中沒有vtblPtr的概念。 –

+0

我的印象是java使用虛擬表來實現多態嗎? – John

+0

這是一個不存在的概念。每個JVM都有自己的方式。它確實如此。 HotSpot編譯器將檢測是否加載了任何子類,或者是否用子類調用代碼,然後決定是否執行虛擬調度,直接調度或甚至內聯代碼。從Java代碼或字節代碼中不可能知道它是如何在特定的時間點完成的。 –

回答

3

這裏是Super類是什麼樣子的JVM(實際上,這是javap -c Super獲得的人類可讀的版本)

class Super { 
    Super(); 
    Code: 
     0: aload_0  
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: aload_0  
     5: invokevirtual #2     // Method print:()V 
     8: return   

    public void print(); 
    Code: 
     0: getstatic  #3     // Field java/lang/System.out:Ljava/io/PrintStream; 
     3: ldc   #4     // String in super 
     5: invokevirtual #5     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     8: return   
} 

,你可以看到,printSuper構造器並沒有在編譯解析時間到Super::print。虛擬調用意味着它在運行時被解析,相對於類this

+0

非常感謝。 – John

+0

這可能是有用的資源添加:http://cs.au.dk/~mis/dOvs/jvmspec/ref--35.html – John

2

我的理解是,在編譯期間,類將獲得V-table pointervtblPtrassociated與屬於一個類的方法。因此超類應該引用它自己的方法print()。

您對虛擬方法和重寫有一個基本的誤解。在Java中,每個非static,非private方法都是虛擬的。虛擬方法的每個虛擬(普通)調用將從任何地方調用與調用該方法的對象的類相關聯的版本。該版本可能會或可能不會被繼承,並且可能會或可能不會覆蓋超類的方法。

this上隱式或顯式執行虛擬方法調用絕不會改變任何這種情況。特別是,初始化對象的實際類對每個構造函數都是可見的,併爲所有方法調用提供上下文。實際上,這就是爲什麼構造函數調用由它自己的類提供的虛擬方法不是一個好主意。超類構造函數在子類構造函數之前運行,所以如果超類構造函數調用恰好被子類覆蓋的方法,那麼該方法將在該方法假定的方式完全初始化之前運行。

+0

感謝您的澄清。 – John