2015-05-29 275 views
1

重寫方法的問題,如果有可能,請讓這句話清楚我調用構造函數從

這裏,筆者說:

不要從構造函數重寫的方法。創建子類對象時,可能會導致在子類對象完全初始化之前被重寫的方法稱爲 。回想一下,當你構造一個子類對象時,它的構造函數首先調用 直接超類的構造函數之一。 如果超類的構造函數 調用一個可重寫的方法,該方法的子類的版本 將由超類構造函數調用 - 在子類 構造函數的主體有機會執行之前。

我不能明白,它如何能夠調用重寫方法的子類的版本在父類的構造函數

TNX

回答

3

您必須首先區分實例化和初始化。實例化是創建類型實例(爲其分配空間並獲取對該空間的引用)的過程。初始化是將實例的狀態設置爲其初始值的過程。

採取以下類型層次:

class Foo { 
    public Foo() {} 
} 
class Bar extends Foo { 
    public Bar() {super();} 
} 

新實例創建表達式

new Bar(); 

原因實例化和初始化。實例化首先發生。 Java創建一個具體類型爲Bar的實例。

然後需要進行初始化。在繼承層次結構中,初始化遵循相同的層次結構,但是自上而下。

Object 
    | 
    Foo 
    | 
    Bar 

Object運行第一初始化那些被定義爲Object部分的狀態的構造,則對於Foo構造運行初始化那些被定義爲Foo部分的狀態,並最終爲Bar構造是運行以初始化Bar中定義的狀態。 您的實例仍屬於Bar所以多態性仍然適用。如果調用一個實例方法,並且該方法在層次結構中較低的地方被覆蓋,那麼該實現將被調用。

這就是引用的意思。這很危險。 Read more here:

What's wrong with overridable method calls in constructors?

1

一個例子,證明孩子的方法將被調用:

class Foo { 
    static class Parent { 
    Parent() { 
     someMethod(); 
    } 
    void someMethod() {} 
    } 

    static class Child extends Parent { 
    @Override void someMethod() { 
     throw new AssertionError("Invoked"); 
    } 
    } 

    public static void main(String[] args) { 
    new Child(); // Throws Exception. 
    } 
} 

輸出:

Exception in thread "main" java.lang.AssertionError: Invoked 
     at Foo$Child.someMethod(Foo.java:16) 
     at Foo$Parent.<init>(Foo.java:9) 
     at Foo$Child.<init>(Foo.java:14) 
     at Foo.main(Foo.java:21) 
2

爲了說明這是一個壞主意了一些(簡單的)代碼的原因,考慮這兩個類:

class Greeter { 
    protected Greeter() { 
     printHello(); 
    } 

    protected void printHello() { 
     System.out.println("Hello"); 
    } 
} 

看起來很簡單,只要你實例化它,它打印Hello。現在,讓我們擴展它:

class NamedGreeter extends Greeter { 
    private String name; 

    public NamedGreeter(String name) { 
     this.name = name; 
    } 

    @Override 
    protected void printHello() { 
     System.out.println("Hello " + name); 
    } 
} 

的目的顯然是有NamedGreeter實例化時的名字跟你打招呼,但其實它總是打印Hello null因爲當NamedGreeter被實例化的超級構造函數首先被調用。

感謝多態是如何工作的,任何時候printHello()方法被調用在NamedGreeter(即使該調用來自Greeter類中)在NamedGreeter實施將被調用。在父類的構造函數中調用該方法意味着即使子類繼承它,也不會初始化子類定義的任何字段,僅僅是因爲在子類構造函數之前無法在子構造函數中執行任何操作(如初始化字段)父構造函數被調用。