Java 超类的重写保护方法
在下面的示例中,为什么字符串b打印null而字符串c打印“gg” 如果我错了,请纠正我,只要子类(BClass)重写了超类(AClass)的受保护方法(即initClass()),如果您实例化了该子类。超类必须使用子类指定的重写方法Java 超类的重写保护方法,java,inheritance,overriding,protected,Java,Inheritance,Overriding,Protected,在下面的示例中,为什么字符串b打印null而字符串c打印“gg” 如果我错了,请纠正我,只要子类(BClass)重写了超类(AClass)的受保护方法(即initClass()),如果您实例化了该子类。超类必须使用子类指定的重写方法 public class Example { public class AClass { private String a; public AClass() { initClass();
public class Example {
public class AClass {
private String a;
public AClass() {
initClass();
}
protected void initClass() {
a = "randomtext";
}
}
public class BClass extends AClass {
private String b = null;
private String c;
@Override
protected void initClass() {
b = "omg!";
c = "gg";
}
public void bValue() {
System.out.println(b); // prints null
System.out.println(c); // prints "gg"
}
}
public static void main(String[] args) {
Example.BClass b = new Example().new BClass();
b.bValue();
}
}
我认为这就是问题的原因:
public class Main {
private static class PrintOnCreate {
public PrintOnCreate(String message) {
System.out.println(message);
}
}
private static class BaseClass {
private PrintOnCreate member =
new PrintOnCreate("BaseClass: member initialization");
static {
System.out.println("BaseClass: static initialization");
}
public BaseClass() {
System.out.println("BaseClass: constructor");
memberCalledFromConstructor();
}
public void memberCalledFromConstructor() {
System.out.println("BaseClass: member called from constructor");
}
}
private static class DerivedClass extends BaseClass {
private PrintOnCreate member =
new PrintOnCreate("DerivedClass: member initialization");
static {
System.out.println("DerivedClass: static initialization");
}
public DerivedClass() {
System.out.println("DerivedClass: constructor");
}
@Override
public void memberCalledFromConstructor() {
System.out.println("DerivedClass: member called from constructor");
}
}
public static void main (String[] args) {
BaseClass obj = new DerivedClass();
}
}
该程序的输出为:
BaseClass: static initialization
DerivedClass: static initialization
BaseClass: member initialization
BaseClass: constructor
DerivedClass: member called from constructor
DerivedClass: member initialization
DerivedClass: constructor
。。。这表明派生类的成员是在基类的构造函数(以及派生类的成员函数的调用完成)之后初始化的。这还说明了从构造函数调用可重写函数的一个关键危险,即该函数可以在它所依赖的类的成员初始化之前调用。因此,构造函数通常应该避免调用成员函数(当它们调用时,这些函数应该是
final
或static
,这样它们要么仅依赖于已初始化的当前类,要么不依赖于任何实例变量).发生这种情况是因为在初始化ClassB
的字段之前调用了超类构造函数。因此调用了initClass()
方法,该方法设置b=“omg!”
,但是当超级类构造函数返回时,b
被初始化为ClassB
中声明的值,即null
要进行调试,请设置一个断点并逐步进行,您会发现b
首先设置为null
,然后更改为omg代码>然后返回到自的null
在本例中,您可以看到执行顺序。第一步是构造函数向下调用对象
构造函数。
之后会发生这种情况:
接下来,执行类[…]的实例变量的所有初始值设定项
由于您的实例变量b被初始化为null,因此之后它将再次为null,这是因为AClass的第一个构造函数将值设置为b=omg代码>和c=gg
。在这之后,当b类在内存中加载时,它设置为b=null
,而c保持原样,即gg,这是因为在b类中,对于b类,您执行声明和初始化,对于c类,您只执行声明,因此,由于c已经在内存中,它甚至不会得到它的默认值,并且由于您没有对c进行任何初始化,它仍然保持其早期状态 关于正在发生的事情,已经给出了几个正确的答案。我只想补充一点,从构造函数调用重写的方法通常是不好的做法(当然,除非您确切知道自己在做什么)。如您所见,在调用其实例方法时,子类可能未完全初始化(子类构造函数逻辑尚未执行,因此在未构造的对象上调用了有效重写的方法,这是危险的),这可能会导致与本问题中描述的混淆
最好在构造函数中编写初始化逻辑,如果太长,则将其划分为从构造函数调用的几个私有方法。这是否解释了为什么c='gg'?何时调用BClass的initClass(),如果调用它,为什么设置c值而不是b值。@Rossdew,你是对的。。。我最初的回答是不正确的。从基类调用可重写成员函数的断言是CORRET,但我原来的推理是错误的(这是C++中的一个属性,显然在java中不一样,在断言之前我应该用一个简单的测试程序来双重检查)。