【问题标题】:Default constructor does not initialize the instance members of the class?默认构造函数不初始化类的实例成员?
【发布时间】:2016-07-27 05:29:55
【问题描述】:

我遇到了一个问题 "以下关于“默认”构造函数的说法正确的是?"

和一个选项 “它初始化类的实例成员。” 是错误的选择。

现在我的理解是,如果我们有这样的代码

    Class Test {
        String name;
    }

然后编译器创建看起来像这样的默认构造函数

    Class Test {
        String name;
        Test(){
            super();
            name = null;
        }
    }

初始化实例成员的默认构造函数不是name=null吗?

【问题讨论】:

标签: java constructor default-constructor


【解决方案1】:

类构造函数不是初始化的,JVM会这样做

创建对象的内存后,对象的成员默认初始化为某个可预测的值,这成为它们的默认值。这都是在调用构造函数之前完成的!

根据specification

  • 每个类变量、实例变量或数组组件在创建时都会使用默认值进行初始化(§15.9§15.10.2):
    • 对于byte类型,默认值为0,即(byte)0的值。
    • 对于short类型,默认值为0,即(short)0的值。
    • 对于int类型,默认值为0,即0
    • 对于long类型,默认值为0,即0L
    • 对于float类型,默认值为正零,即0.0f
    • 对于double类型,默认值为正零,即0.0d
    • 对于char类型,默认值为空字符,即'\u0000'
    • 对于布尔类型,默认值为false
    • 对于所有引用类型 (§4.3),默认值为 null

您的假设很接近,但事实是,在构造函数参数被评估之前,甚至在它可以为每个字段分配值之前 - 这些字段已经保存了它们的默认值,这是由 JVM 完成的。

阅读§15.9.4小节,了解初始化过程是如何进行的

【讨论】:

  • 这是否意味着对于编写良好的非默认构造函数,您的成员会被初始化,然后它们的值会立即再次更改?似乎效率不高。
  • @LightnessRacesinOrbit 并非所有非默认构造函数都会初始化所有成员。 写得好是一个值得尝试和评估的有趣属性。
  • @LightnessRacesinOrbit:我只贡献了一个 Java 实现,那是很多年前的事了,但我们这样做的方式是分配归零内存(IIRC 销毁时 gc 归零内存)。所以是的,在某种意义上,与 C 或 C++ 相比效率低下,其中存在未初始化内存之类的东西。你付出的代价是,Java 中没有这样的东西。但这并没有将0 显式分配给即将被覆盖的byte 数据成员那么糟糕。都是memset-like,通常在低优先级线程中。不知道甲骨文是不是也是这样。
  • 无论如何我的观点只是“在创建对象的内存之后,对象的成员被默认初始化”是对需求的名义上的“as-if”描述。
  • @Steve:嗯,好吧。
【解决方案2】:

在 Java 中,字段在构造函数之前之前初始化。这可以通过以下代码轻松证明:

public class MyClass {

    int myField = initMyField();

    MyClass(){
        System.out.println("ctor");
    }

    static int initMyField() {
        System.out.println("init field");
        return 1;
    }
}

输出

init field
ctor

也可以查看反编译后的代码。

【讨论】:

  • -1 这并不能证明什么。由于隐含的super(),初始化可以在构造函数的开始处编织。
  • 您实际上展示的是不同的东西:您展示的初始化实际上是移到类的一个初始化块中,在所有参数的初始 0 初始化之后执行(由 JVM 完成)在为对象分配内存之后)并在这两个完成之后调用构造函数。
  • myField, initMyField。注意小写。
  • 如果你把你的[iI]nitMyField变成一个实例方法,你可以让它在设置之前显示this.myField实际上是0。
【解决方案3】:

默认构造函数不是在初始化实例成员name = null吗?

不,构造函数被调用所有实例变量被初始化为默认值:0primitive numerical types 的等效值,false 用于boolean 类型,@987654327 @ 代表reference types

【讨论】:

    【解决方案4】:

    不,它不是为您初始化实例变量的默认构造函数。每种类型都有一个默认值。在您创建对象的那一刻,使用默认值。

    因此,如果您不显式初始化实例变量,它们仍将使用为它们隐式定义的默认值。

    即0 表示 int,null 表示引用类型.. 等等

    但是,值得注意的是,我们不应该理所当然地认为给定了一个默认值,而选择不初始化变量。


    你可以尝试定义一个空的构造函数,用空实现覆盖默认构造函数。您将意识到所有实例变量仍将被初始化。

    【讨论】:

      【解决方案5】:

      确实如此。虽然这个问题更多地基于使用情况。

      public class Main {
          String x;
      
          Main() {
              x = "Init";
          }
      
          @Override
          public String toString() {
              return x;
      
          }
      
          public static void main(String[] args) {
              System.out.println(new Main());
          }
      
      }
      

      输出:

      Init
      

      【讨论】:

        【解决方案6】:

        每当我们执行一个 java 类时,首先会执行静态控制流。在静态控制流中,如果我们正在创建一个对象,那么将执行以下步骤(按上述顺序)作为 Inatance 控制流的一部分:

        1. 从上到下标识实例成员(实例变量和实例块)。
        2. 执行实例变量赋值和实例块。
        3. 构造函数的执行。

        因此,在您的上述代码中,即使在构造函数执行之前,实例变量“name”已经分配给 null(引用类型的默认值)。

        【讨论】:

          【解决方案7】:

          默认构造函数为对象提供默认值,通常在没有显式定义构造函数时由编译器创建。例如

          class DefaultTest{  
          int id;  
          String name;  
          
          void display(){System.out.println(id+" "+name);}  
          
          public static void main(String args[]){  
          DefaultTest s1=new DefaultTest();  
          DefaultTest s2=new DefaultTest();  
          s1.display();  
          s2.display();  
          }  
          }  
          

          注意:没有定义构造函数,编译器将生成一个默认构造函数,该构造函数将为两个对象分配 0 个空值。

          【讨论】:

            猜你喜欢
            • 2011-08-09
            • 1970-01-01
            • 1970-01-01
            • 2016-04-05
            • 2015-07-03
            • 2015-10-07
            • 1970-01-01
            • 2018-11-24
            • 1970-01-01
            相关资源
            最近更新 更多