Java 访问构造函数中的实例成员
我在一本书中读到,只有在超级构造函数运行之后才能访问实例成员 我偶然发现了以下代码:Java 访问构造函数中的实例成员,java,constructor,instance-variables,super,Java,Constructor,Instance Variables,Super,我在一本书中读到,只有在超级构造函数运行之后才能访问实例成员 我偶然发现了以下代码: class Parent { Parent() { printIt(); } void printIt() { System.out.println("I'm in a overridden method. Great."); } } class Child extends Parent { int i = 100; pu
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
一百
我的问题是:
如果实例成员只有在超级构造函数运行后才可访问,那么为什么在执行类Parent的printIt()方法时(由于多态性,它实际上是子类的printIt()),即使父级的构造函数尚未完成执行,它仍然能够访问子级的未初始化实例变量i
我错过了什么
即使父级的构造函数尚未完成执行,它仍然能够访问子级的未初始化实例变量i
您可以访问它,但在它初始化之前(这不是您通常想要的)
变量的“空格”已经存在(毕竟您确实有一个实例),但将其初始化为正确起始值的代码尚未运行。因此,它将全部为null、false和0
因此,类中的一个方法(“printIt”)在对象生命周期的一个尴尬时刻被调用(在初始化器运行之前,在“半成品”实例上)。这就是你读到的警告想要说的
我在一本书中读到,只有在超级构造函数运行之后才能访问实例成员
你的书错了(如果它真的这么说的话)。一旦施工开始,他们随时都可以进入。但是,它们在超级构造函数运行之后才会初始化。因此,您打印的是默认值:null、zero或false。重写发生在代码中。对象在运行时被考虑在内。因此,调用了Child的printIt()。此时,“i”的值未知,但具有默认值“0”,因为它是一个实例变量。完成后,调用p.printIt(),调用Child的printIt(),此时读取int i=100并打印100
因此,输出应该并且将是01100我认为您的示例误导了您。事实上,超级构造函数以前运行过,您可以通过下面的修改示例看到这一点。作为澄清,成员值是可访问的,但可能尚未初始化
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 + " ");
}
}
首次创建对象时,对象中的字段被初始化为默认值null或0,然后构造函数实际运行,这将调用超级构造函数作为第一步 遗憾的是,您不能通过将构造函数编写为
Child() {
i=100;
super();
}
如果不能做到这一点,就无法在父构造函数的重写方法调用中使用childsi
字段之前设置它
不过,值得了解一些解决此问题的方法:
另一种方法是将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);
另一种方法是将printIt
与父/子继承人分离。那你可以
在调用父构造函数之前创建打印机。(通常,这种技巧可以用来完全去除子类,只留下父类和组件——也就是说,最终使用的是组合而不是继承。)
“一旦构造开始,它们就可以随时访问。但是,它们直到超级构造函数运行之后才被初始化。”---这一点很好+1.
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() );
}
}