我对如何在运行时调用方法和构造函数感到困惑,因为派生的构造函数打印了 3 次,高度打印了 0 次
我尝试在方法和构造函数中打印一些消息,以了解到底发生了什么
public class Derived extends Base{
public static void main(String args[]){
System.out.println("Hello World");
Derived d = new Derived();
}
protected Derived(){
System.out.println("Inside Derived Const");
showAll();
}
protected void showAll(){
System.out.println("Inside Derived showAll");
System.out.println(getClass().getName()+" : "+height);
}
double height = 106.0;
}
class Base{
protected Base(){
System.out.println("Inside Base Const");
showAll();
}
protected void showAll(){
System.out.println("Inside Base showAll");
System.out.println(getClass().getName()+" : "+height);
}
double height = 196.0;
}
我预计输出是
Hello world
Derived : 106
Base : 196
相反,我得到了
Hello World
Inside Base Const
Inside Derived showAll
Derived : 0.0
Inside Derived Const
Inside Derived showAll
Derived : 106.0
这是因为您从类中派生了Derived
类Base
并隐藏了变量并重写了方法。
每当使用以下命令实例化派生类时,都会调用基类的构造函数:
Derived d = new Derived();
下面是调用上述代码时会发生什么:
- 基类
Base()
的构造函数称为, - 然后打印
"Inside Base Const"
, - 不调用方法
showAll()
,因为它被重写。改为调用Derived
类内的方法showAll()
, - 打印
"Inside Base showAll"
, - 打印
"Derived : 106.0"
是因为基类内部double height = 196.0;
被Derived
类内部的double height = 106.0;
阴影。
您偶然发现了Java(和其他面向对象语言)工作中的一个主要缺陷。 编译器允许存在此缺陷,但是任何体面的IDE都会检测到它并向您显示警告,因此您发布此问题的事实告诉我,您在编译时没有启用一些非常重要的警告。 在 IDE 上启用尽可能多的警告,IDE 会向您指出问题所在。 但无论如何,我现在都会解释它。
在基类的构造函数中,您正在调用showAll()
。 这是一个非最终方法,因此任何派生类都可以重写它。
从构造函数中调用可重写的方法是一个严重的错误。
您会看到,在调用基类构造函数的那一刻,派生类构造函数尚未被调用,并且对象尚未完全初始化。 因此,简单地说,在基类构造函数正在运行的那一刻,派生类的语句double height = 106.0;
尚未执行。 这就是为什么派生类的height
成员似乎包含零的原因。 零只是默认值,106.0
尚未存储在其中。
如果在 IDE 中启用了正确的警告,则 IDE 将警告您从构造函数中调用任何非最终方法。 (在我看来,这不应该是一个警告,而应该是一个错误。
因此,让我简要介绍一下您的每个期望与结果:
您期望满足以下条件:
Hello world
Derived : 106
Base : 196
这是不对的。如果您没有其他错误,您应该预料到以下内容:
Hello world
Base : 196
Derived : 106
这是因为您的Derived
构造函数以对Base
构造函数的隐式(隐藏)调用开头,因此将始终首先调用基构造函数。 你System.out.println("Inside X Const")
发言就证明了这一点。
但相反,你得到了以下内容:
Hello World
Derived : 0.0
Derived : 106.0
这两行似乎都来自"派生"类,因为getClass()
将返回当前对象的类,而您只创建了一个对象,它是Derived
的实例。 因此,当从Derived
类中调用时,getClass()
将返回Derived
,当从Base
类中调用时,它也将返回Derived
。
然后,我已经解释的0.0
来自Derived
实例的未初始化成员变量,因为您正在从基类的构造函数中调用可覆盖的showAll()
,因此派生类在调用该方法的那一刻没有机会初始化自身。
需要注意的重要一点是,当创建子类对象时,不会创建超类对象的单独对象。
仅创建具有超类变量的子类对象。
因此,我们不能盲目地说,无论何时执行类构造函数,该类的对象是否被创建。请参考以下更改并查看。
public class Derived extends Base {
public static void main(String args[]) {
System.out.println("Hello World");
Derived d = new Derived();
d.getClass();
}
protected Derived() {
System.out.println("Inside Derived Const");
System.out.println("Sub class object hashcode :" + this.hashCode());
System.out.println(this.getClass().getName());
showAll();
}
protected void showAll() {
System.out.println("Inside Derived showAll");
System.out.println("Sub class object hashcode :" + this.hashCode());
System.out.println(getClass().getName() + " : " + height);
}
double height = 106.0;
}
class Base {
protected Base() {
System.out.println("Inside Base Const");
System.out.println("Super class object hashcode :" + this.hashCode());
System.out.println(this.getClass().getName());
showAll();
}
protected void showAll() {
System.out.println("Inside Base showAll");
System.out.println("Sub class object hashcode :" + this.hashCode());
System.out.println(getClass().getName() + " : " + height);
}
double height = 196.0;
}
输出
Hello World
Inside Base Const
Super class object hashcode :1917513796
Derived
Inside Derived showAll
Sub class object hashcode :1917513796
Derived : 0.0
Inside Derived Const
Sub class object hashcode :1917513796
Derived
Inside Derived showAll
Sub class object hashcode :1917513796
Derived : 106.0
正如我们可以观察到的,超类(Base)对象哈希码和 子类(派生)对象哈希码是相同的,所以只有一个对象是 创建。
这个对象是类派生的,就像我们尝试打印类的名称一样 创建哪个对象,它正在打印子类的"派生"。
当你第一次在超级类中调用showAll()时 由于该方法,没有高度变量的值 显示全部已覆盖。但是该值尚未在它调用的那一行分配。
当方法 showAll() 在子类中调用时,它具有 分配的值为 196.0。这是由于变量隐藏*。
(变量隐藏:当子类和父类都具有同名变量时,子类的变量将隐藏父类的变量。