为什么程序打印高度值 0 而不是我设置的高度值



我对如何在运行时调用方法和构造函数感到困惑,因为派生的构造函数打印了 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

这是因为您从类中派生了DerivedBase并隐藏了变量并重写了方法。

每当使用以下命令实例化派生类时,都会调用基类的构造函数:

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。这是由于变量隐藏*。

(变量隐藏:当子类和父类都具有同名变量时,子类的变量将隐藏父类的变量。

最新更新