【问题标题】:Why does the program print the height value 0 instead of the one I set?为什么程序打印高度值 0 而不是我设置的?
【发布时间】:2019-12-07 10:57:45
【问题描述】:

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

【问题讨论】:

  • 将高度作为参数添加到方法中,并在调用方法时传递值。就像 showAll(double height){ System.out.println("height: " + height }
  • 主函数不应该在类中
  • 当你试图打印出Derived::height的值时,对象还没有完全实例化
  • @TymekWojnarowski 为什么不呢?只是一个设计问题,对给定的问题没有影响
  • 实例初始化器将在对象初始化后执行(即在构造函数调用后)希望这会有所帮助:docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5

标签: java oop inheritance constructor output


【解决方案1】:

这是因为您从 Base 类派生了 Derived 类并隐藏了变量并覆盖了方法。

每当您实例化派生类时,您都会调用基类的构造函数:

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; 遮蔽。

【讨论】:

  • 并显示“Derived: 0.0”,因为从 Base 构造函数调用 showAll 时高度(在 Derived 中)尚未初始化
  • 这个答案是错误的。成员变量的阴影不适用于该问题。 “Derived : 106.0”由 base 类的 showAll() 方法打印,因为 getClass().getName() 将返回“Derived”,即使它是从基类的方法中调用的。跨度>
【解决方案2】:

需要注意的重要一点是,在创建子类对象时,不会创建超类对象的单独对象。

仅创建具有超类变量的子类对象。

所以我们不能盲目地说,无论何时执行类构造函数,都会创建该类的对象。请参考以下更改并查看。

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
  • 我们可以观察到超类(基)对象哈希码和 子类(派生)对象哈希码相同,因此只有一个对象 创建的。

  • 当我们尝试打印类名时,该对象属于派生类 创建了哪个对象,它正在打印作为子类的“Derived”。

  • 当你第一次在超类中调用 showAll() 由于该方法,高度变量没有值 showAll 已被覆盖。但该值尚未在它调用的那一行分配。

  • 当方法 showAll() 在子类中调用时,它具有 分配的值是 196.0。这是由于变量隐藏*。

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

【讨论】:

    【解决方案3】:

    您偶然发现了 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
    

    这两行似乎都来自“Derived”类,因为getClass() 将返回当前对象的类,而您只创建了一个对象,它是Derived 的一个实例。因此,getClass() 在从 Derived 类中调用时将返回 Derived,并且在从 Base 类中调用时也将返回 Derived

    然后我已经解释过的0.0 来自Derived 实例的未初始化成员变量,因为您正在从基类的构造函数中调用showAll() 可重写,因此派生类具有在调用该方法的那一刻没有机会初始化自身。

    【讨论】:

    • 感谢您帮助我,但这只是我在采访中遇到的一个问题,我在 sublime text 和 windows powershell 上运行此代码
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 1970-01-01
    • 2016-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-25
    相关资源
    最近更新 更多