2012-08-27 74 views
3

我在一本書中讀到實例成員只有在超級構造函數運行後纔可以訪問。訪問構造函數中的實例成員

我偶然發現了下面的代碼:

class Parent { 

    Parent() { 
     printIt(); 
    } 

    void printIt() { 
     System.out.println("I'm in a overridden method. Great."); 
    } 
} 

class Child extends Parent { 

    int i = 100; 

    public static void main(String[] args) { 
     Parent p = new Child(); 
     p.printIt(); 
    } 

    void printIt() { 
     System.out.print(i + " "); 
    } 
} 

和它打印:

我的問題是:

如果實例成員都可以訪問後,才超級構造函數運行,那麼爲什麼在執行父類的printIt()方法時(它實際上是Child的printIt()到多態性),它能夠訪問Child的未初始化的實例變量i,即使Parent的構造函數尚未完成執行?

我錯過了什麼?

回答

7

我讀了一本書,只有超級構造函數運行後才能訪問實例成員。

你的書是錯的(如果這就是它說的)。一旦施工開始,他們隨時都可以進入。然而,他們不是初始化,直到超級構造函數運行後。所以你打印的是默認值:null,零或false。

+2

「他們在任何時候都可以訪問一旦施工已經開始,但它們不會在超級構造函數運行之後才初始化。「---偉大的觀點。 +1 –

+0

很好說@EJP。事情現在變得更加清晰。 – amor214

3

即使Parent的構造函數尚未完成執行,它能夠訪問Child的未初始化實例變量i?

你能夠訪問它,但在它被初始化之前(這不是你通常想要的)。

變量的「空格」已經存在(畢竟你確實有一個實例),但是將它初始化爲正確的起始值的代碼還沒有運行。因此,它將全爲空,虛假和0.

因此,類(「printIt」)中的方法在對象的生命週期中的一個尷尬點被調用(在初始化器運行之前,在一個「半完成」的實例)。這是你讀的警告想要說的。

0

覆蓋發生在您的代碼中。對象在運行時會被考慮到。所以,小孩的printIt()被叫。此時,'i'的值未知,但默認值爲'0',因爲它是一個實例變量。一旦做到這一點,p.printIt()被調用時,爲printit()兒童的被調用,通過該時間INT I = 100被讀取並且它打印100

因此輸出應該和將

2

我認爲你的例子會誤導你。實際上,超級構造函數在之前運行,您可以通過下面的修改示例來看到它。同樣作爲澄清,成員值是可訪問的,但它們可能尚未初始化。在對象

class Parent { 

    int i = 0; 

    Parent() { 
     i = 1; 
     printIt(); 
    } 

    void printIt() { 
     System.out.println("I'm in a overridden method. Great. i = " + i); 
    } 
} 

class Child extends Parent { 
    public static void main(String[] args) { 
     Parent p = new Child(); 
     p.printIt(); 
    } 

    void printIt() { 
     System.out.print(i + " "); 
    } 
} 
0

字段被初始化爲null值或默認值0第一次創建對象時,那麼你的構造實際運行,它調用超級構造函數的第一步。

可悲的是,你不能變通的作法是寫你的構造函數

Child() { 
    i=100; 
    super(); 
} 

沒有它獲取覆蓋方法使用前能夠做到這一點有沒有辦法來設置孩子的i場來自父母構造函數的調用。

這是值得了解的一些方法來解決這個雖然:

上的做法是隱藏i一個抽象的getter後面,並提供創建新實例覆蓋getI一個靜態工廠函數。

public class Child extends Parent { 

    protected abstract getI(); 

    @Override void printIt() { 
    System.out.print("i = " + i); 
    } 

    static Child create(final int i) { 
     return new Child() { 
     int getI() { return i; } 
     } 
    } 
} 

Child child = Child.create(100); 

另一種方法是從父/子heirachy解耦printIt。然後你可以在調用Parent構造函數之前創建打印機 。 (通常,這種伎倆可用於腸道孩子完全離開你只與父類和組件 - 你最終使用組成,而不是繼承)

class Parent { 
    public interface Printer { 
    void printIt(); 
    } 

    public class DefaultPrinter extends Printer { 
    @Override void printIt() { 
     System.out.println("Default Printer..."); 
    } 
    } 

    Parent() { 
    this(new DefaultPrinter()); 
    } 

    Parent(Printer p) { 
    this.printer = p; 
    printIt(); 
    } 

    void printIt() { 
    p.printIt(); 
    } 
} 

public class Child extends Parent { 
    public class ChildPrinter implements Parent.Printer { 
    final int i = 100; 
    @Override void printIt() { 
     System.out.println("i = "+i); 
    } 
    } 

    Child() { 
    super(new Printer()); 
    } 
} 
相關問題