当前位置: 首页 > 知识库问答 >
问题:

在构造函数中,为什么在执行超类构造函数之前不能引用当前类的字段?[副本]

赫连瑾瑜
2023-03-14

例如,

Subclass extends ParentClass {
  private String subclassField;
  Subclass() {
    // it's illegal
    super(subclassField);
  }
}

这将引入编译错误“调用构造函数时无法引用实例字段”。

StackOverflow中有关此场景的类似问题的一些答案转到“因为当前实例仍在构建中”或“堆中尚未创建的实例”。

然而,令人困惑的是,在超类构造函数中,可以调用可重写的成员方法,这些方法可能访问子类当前实例的字段。在Java也没问题。

我的问题是

>

  • 在实例构造过程中,子类的字段从哪一刻开始可以引用?

    “在调用构造函数时不能引用实例字段”,而在超类构造函数中调用可重写方法是可以的,这背后的理论依据是什么?

  • 共有1个答案

    柴岳
    2023-03-14

    在实例构造过程中,子类的字段从哪一刻开始可以被引用?

    从完成对超类构造函数的调用开始。请记住,在此之前,实例字段将只具有默认值 ;-即使您对它们使用了初始化器或实例初始化块;在这两种情况下,执行此操作的代码都插入到类的构造函数中,位于超类构造函数调用之后。想想这对类:

    class ParentClass {
        ParentClass(String s) {
        }
    }
    
    class Subclass extends ParentClass {
        private String subclassField = "init";
    
        Subclass() {
            super("bar");
        }
    }
    

    如果我们通过javap-c subclass查看subclass的字节码,我们会看到:

    class Subclass extends ParentClass {
      Subclass();
        Code:
           0: aload_0
           1: ldc           #1                  // String bar
           3: invokespecial #2                  // Method ParentClass."":(Ljava/lang/String;)V
           6: aload_0
           7: ldc           #3                  // String init
           9: putfield      #4                  // Field subclassField:Ljava/lang/String;
          12: return
    }
    

    请注意,它编译了我们的代码,就好像我们是这样编写的:

    class Subclass extends ParentClass {
        private String subclassField;
    
        Subclass() {
            super("bar");
            this.subclassField = "init"; // *** Note this moved
        }
    }
    

    (是的,如果您好奇的话,如果有多个构造函数,那么在每个构造函数中都重复该代码。)

    “在调用构造函数时不能引用实例字段”,而在超类构造函数中调用可重写方法是可以的,这背后的理论依据是什么?

    你得问问詹姆斯·高斯林。但是,与实例字段不同的是,实例方法在调用超类构造函数之前具有有用的值,事实上,在构造过程中使用方法有时会非常有用。然而,使用可重写的方法是一种糟糕的做法;您在构造过程中使用的任何方法(直接或间接)都应该是最终的或私有的(例如,有效地最终的)。编译器不会为您做这种区分;也许如果现在就开始制定规则,它就会这样做。

     类似资料:
    • 可能的重复: 为什么this()和super()必须是构造函数中的第一条语句? 为什么子类构造函数必须显式调用超类构造函数?这是什么原因呢?

    • 我目前正在学习Java的继承,我在理解它方面遇到了很大的困难,但是我不能理解的一个主要问题是为什么在下面的示例中有必要调用一个超类构造函数,它有什么帮助?此示例来自Oracle的方法教程。 我最初的想法是,会缩短当前构造函数中参数的代码,它只会接受父类中传入的值并将其添加到子类中(如下所示): 但是,正如我已经知道的那样,这是错误的,并且再次将我带到了真正做什么的问题上。我会非常感谢一些形式的解释

    • 我最近遇到了这种不同寻常的Java语法。。。下面是一个例子: 注意

    • 若类有参数化的构造函数,为什么Java不提供默认构造函数?考虑下面的例子 这里我明确需要添加默认构造函数。有什么原因,为什么Java没有为具有参数化构造函数的类提供默认构造函数?

    • 我有以下2个类 然后运行 或 始终给予 为什么会出现这种情况?乍一看,在这两种场景中,我都假设只调用构造函数,因此唯一的输出是 但这显然是错误的。

    • 问题内容: 我一直在寻找为什么不应该在类的构造函数中调用线程的start方法的理由。考虑以下代码: ImportantData是一些通用的东西(可能很重要),而MyOperationThread是知道如何处理SomeClass实例的线程的子类。 脚节点: 我完全理解为什么这是不安全的。如果MyOperationThread在以下语句完成(并且数据已初始化)之前尝试访问SomeClass.data,