当前位置: 首页 > 面试题库 >

基类构造函数在Java中调用重写方法时派生类对象的状态

柴声
2023-03-14
问题内容

请参考下面的Java代码:

class Base{
     Base(){
         System.out.println("Base Constructor");
         method();
     }
     void method(){}    
}

class Derived extends Base{
    int var = 2;
    Derived(){
         System.out.println("Derived Constructor");  
    }

     @Override
     void method(){
        System.out.println("var = "+var);
     }
 }

class Test2{
    public static void main(String[] args) {
        Derived b = new Derived();
    }
}

看到的输出是:

Base Constructor
var = 0
Derived Constructor

我认为发生var =
0是因为派生对象已初始化一半;类似于乔恩·斯基特在这里说的话

我的问题是:

如果尚未创建Derived类对象,为什么要调用重写的方法?

var在什么时间点分配值为0?

是否有任何需要这种行为的用例?


问题答案:
  • Derived对象 被创建-它只是在构造函数尚未运行。对象的类型在创建后即刻就不会在Java中更改,这发生在所有构造函数运行之前。

  • var在运行构造函数之前,在创建对象的过程中,将默认值指定为0。基本上,类型引用被设置,表示对象的其余内存被擦除为零(从概念上讲,无论如何,作为垃圾回收的一部分,它可能已经被擦除为零)。

  • 这种行为至少会导致一致性,但可能会很痛苦。在一致性方面,假设您有一个可变基类的只读子类。基类可能具有isMutable()有效地默认为true 的属性-但子类覆盖了它,始终返回false。在子类构造函数运行之前,对象是可变的,但是之后是不可变的,这是很奇怪的。另一方面,当您在某个类的构造函数运行之前最终在一个类中运行代码的情况下,这 绝对是 很奇怪的:(

一些准则:

  • 尽量不要在构造函数中做很多工作。避免这种情况的一种方法是在静态方法中进行工作,然后使静态方法的最后一部分成为仅设置字段的构造函数调用。当然,这意味着您在进行工作时不会获得多态的好处-​​但是在构造函数调用中这样做还是很危险的。

  • 尽量避免在构造函数期间调用非最终方法-这很可能引起混乱。记录所有方法调用你真的 必须 做出 非常 清楚,让任何人都重写他们知道,初始化完成之前,他们将被调用。

  • 如果必须在构造期间调用方法,则 通常 不适合事后再调用它。如果是这种情况,请记录下来并尝试在名称中指出。

  • 首先,尽量不要过度使用继承-仅当您有一个继承自Object以外的超类的子类时,这才成为问题:)继承的设计很棘手。



 类似资料:
  • 问题内容: 我有以下代码片段: 执行代码时出现异常: 我不明白为什么会有和例外。有人可以帮助我理解吗? 您可以在此处检查代码。 问题答案: 创建一个对象,这意味着首先调用其超类构造函数,然后依次调用-但您已覆盖它,因此它是该方法的子版本。在该方法中,您调用尚未初始化的。 结论:在构造函数中调用可重写方法几乎从来不是一个好主意。

  • 主要内容:构造函数的调用顺序,基类构造函数调用规则前面我们说基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数, 类的构造函数不能被继承。构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。 在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法

  • 问题内容: 我有两个Java类:B,它扩展了另一个类A,如下所示: 我想打电话给。我来自C++界,我不知道如何用Java做这个基本的事情。 问题答案: 您要寻找的关键字是。例如,请参阅本指南。

  • 当派生类不是立即派生,而是从已经派生的类派生时,我对覆盖函数感到困惑。 在这两种情况下,都调用了最派生的析构函数。我想知道是否只需要大多数基类有一个声明为虚拟的析构函数,在这种情况下,从它继承的所有其他类都有虚拟析构函数,覆盖上游的所有其他析构函数,如果你明白我的意思的话。 我不确定我是否有意义,基本上,如果我有一系列10个类,每个类都从上一个继承,那么链中的任何析构函数都会覆盖所有比它更基础的析

  • 我可以写: 但这当然不起作用,因为调用返回的是而不是我想要的。 我可以通过在(以及和等)中键入类型并抑制警告来避免这个问题,但是即使只有(比方说)10个扩展的实体类,这仍然是为了键入而编写的样板代码。此外,我认为,如果我必须为每个派生类编写代码(无论多么小)(也会有其他类似的方法),那么拥有类层次结构的好处就会丧失很多。 对此有更好的解决方案吗? 更新:使用Java7。

  • 问题内容: 我正在寻找此代码将输出: 但是我得到了: 我了解在B的上下文中,A.f1(String)中的“ this”是B的实例。我是否可以选择执行新的链B1()。f1(String)->(A’s)f1(String)->(A’s)f1(int,String)? 这是一个理论问题,实际上,解决方案显然是在A中实现f1(String)和f1(int,String)都将调用的私有函数。 谢谢你, 马克