我在一本书中读到只有在超级构造函数运行后才能访问实例成员。
我偶然发现了以下代码:
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 + " ");
}
}
并打印:
0 100
我的问题是:
如果实例成员只有在超级构造函数运行后才可访问,那么为什么在执行类Parent的printIt()方法时(由于多态性实际上是Child的printIt()),它能够访问Child的未初始化的实例变量i,即使Parent的构造函数尚未完成执行?
我错过了什么?
答案 0 :(得分:7)
我在一本书中读到只有在超级构造函数运行后才能访问实例成员。
你的书是错的(如果这就是它真正说的那样)。一旦施工开始,它们随时都可以使用。但是,在超级构造函数运行之后,它们才会初始化。所以你打印的是默认值:null,zero或false。
答案 1 :(得分:3)
它能够访问Child的未初始化的实例变量i,即使Parent的构造函数尚未完成执行?
您可以访问它,但在它初始化之前(这不是您通常想要的)。
变量的“空间”已经到位(毕竟你确实有一个实例),但是将它初始化为正确的起始值的代码还没有运行。所以它都是null,false和0。
因此,类中的方法(“printIt”)在对象生命周期的一个尴尬点(在初始化程序运行之前,在“半完成”实例上)被调用。这就是你读过的警告想说的。
答案 2 :(得分: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 + " ");
}
}
答案 3 :(得分:0)
覆盖发生在您的代码中。在运行时考虑对象。因此,调用了儿童的printIt()。此时,'i'的值未知,但默认值为'0',因为它是一个实例变量。完成后,调用p.printIt(),调用Child的printIt(),此时读取int i = 100并打印100.
因此输出应该 0 100
答案 4 :(得分:0)
首次创建对象时,对象中的字段初始化为默认值null或0,然后构造函数实际运行,这将调用超级构造函数作为其第一步。
遗憾的是,无法通过将构造函数编写为
来解决此问题Child() {
i=100;
super();
}
如果没有这样做,就无法设置子i
字段,然后才能在父项构造函数的覆盖方法调用中使用它。
值得了解一些方法来解决这个问题:
方法是隐藏抽象getter后面的i
,并提供一个静态工厂函数,用于创建覆盖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);
另一种方法是将printIt
与父/子heirachy分离。然后你可以
在调用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() );
}
}