Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
基类构造函数在Java中调用重写方法时派生类对象的状态_Java_Inheritance_Constructor - Fatal编程技术网

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

基类构造函数在Java中调用重写方法时派生类对象的状态,java,inheritance,constructor,Java,Inheritance,Constructor,请参考下面的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

请参考下面的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是因为派生对象被半初始化;类似于什么

我的问题是:

如果还没有创建派生类对象,为什么要调用重写的方法

var在什么时间点赋值为0

是否存在需要此类行为的用例?

  • 派生的
    对象已经创建-只是构造函数还没有运行。在Java中,对象的类型在创建之后不会改变,这发生在所有构造函数运行之前

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

  • 这种行为至少会带来一致性,但也会带来痛苦。在一致性方面,假设您有一个可变基类的只读子类。基类可能有一个
    isMutable()
    属性,该属性实际上默认为true,但子类将其重写以始终返回false。在子类构造函数运行之前,对象是可变的,但之后是不可变的,这是很奇怪的。另一方面,当您在类的构造函数运行之前在类中运行代码时,这种情况肯定很奇怪:(

一些准则:

  • 尽量不要在构造函数中做太多的工作。避免这种情况的一种方法是在静态方法中工作,然后使静态方法的最后部分成为一个只设置字段的构造函数调用。当然,这意味着您在工作时不会得到多态性的好处,但在构造函数调用中这样做是危险的反正是我们

  • 在构造过程中尽量避免调用非final方法-这很可能会造成混乱。请非常清楚地记录您必须进行的任何方法调用,以便覆盖它们的任何人都知道它们将在初始化完成之前被调用

  • 如果您必须在构造过程中调用某个方法,那么通常不适合在之后调用它。如果是这种情况,请记录它并尝试在名称中指明它

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


为了解释这种行为,应该注意Java语言规范的一些属性:

  • 超类构造函数总是在子类构造函数之前隐式/显式调用
  • 来自构造函数的方法调用与任何其他方法调用一样;如果方法是非final的,则调用是虚拟调用,这意味着要调用的方法实现是与对象的运行时类型关联的方法实现
  • 在执行构造函数之前,所有数据成员都会自动初始化为默认值(0表示数字原语,null表示对象,false表示布尔值)
事件顺序如下:

  • 创建子类的一个实例
  • 所有数据成员都使用默认值初始化
  • 被调用的构造函数立即将控制委托给相关超类的构造函数
  • 超级构造函数初始化它自己的一些/所有数据成员,然后调用一个虚拟方法
  • 该方法由子类重写,因此调用子类实现
  • 该方法尝试使用子类的数据成员,假设它们已经初始化,但事实并非如此-调用堆栈尚未返回到子类的构造函数 简而言之,每当一个超类的构造函数调用一个非final方法时,我们就有进入这个陷阱的潜在风险,因此不建议这样做。 请注意,如果坚持这种模式,就没有优雅的解决方案。这里有两个复杂的和有创意的,都需要线程同步(!):

    为什么被重写的方法会 如果派生类对象 还没有创建吗

    Derived
    类构造函数隐式调用
    Base
    类构造函数作为第一条语句
    Base
    class构造函数调用
    method()
    ,该构造函数调用
    Derived
    类中被重写的实现,因为该类是正在创建其对象的类
    method()
    Derived
    类中,此时将
    var
    视为0

    在什么时间点分配var 值0

    var
    被指定为
    int
    类型的默认值,即在调用
    Derived
    类的构造函数之前为0。在隐式超类构造函数调用完成之后和派生的类的构造函数中的语句开始执行之前,它被赋值为2

    是否存在这样的用例 行为是需要的吗


    在非
    final
    类的构造函数/初始值设定项中使用非
    final
    private
    方法通常是个坏主意。原因在您的代码中很明显。如果正在创建的对象是子类实例,则方法可能会给出意想不到的结果。

    注意,这与C++不同,在该类型中,当对象正在构建时,类型会发生变化,因此调用基类构造函数的虚方法不调用派生类的重写。同样的事情