2012-08-27 30 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()); 
    } 
} 
相关问题