【问题标题】:What happens when subclasses don't define a constructor in Java?当子类没有在 Java 中定义构造函数时会发生什么?
【发布时间】:2009-12-10 03:30:36
【问题描述】:

我有几个我想知道的案例。首先,如果你没有构造函数:

class NoCons { int x; }

当我执行new NoCons() 时,会调用默认构造函数。它究竟是做什么的?它会将x 设置为 0,还是在其他地方发生这种情况?

如果我有这种情况怎么办:

class NoCons2 extends NoCons { int y; }

当我拨打new NoCons2() 时会发生什么? NoCons 的默认构造函数是否被调用,然后是 NoCons2 的构造函数?他们是否都将各自的 xy 字段设置为 0?

这个版本怎么样:

class Cons2 extends NoCons { int y; public Cons2() {} }

现在我有一个构造函数,但它不调用超类的构造函数。 x 是如何被初始化的?如果我遇到这种情况怎么办:

class Cons { int x; public Cons() {} }
class NoCons2 extends Cons { int y;  }

会调用Cons 构造函数吗?

我可以尝试所有这些示例,但我无法确定默认构造函数何时运行。考虑这个问题的一般方法是什么,以便我知道未来情况会发生什么?

【问题讨论】:

    标签: java generics oop


    【解决方案1】:

    当 Java 类没有明确定义的构造函数时,会添加一个公共的无参数默认构造函数:

    class Cons { int x; } 
    

    相当于:

    class Cons { int x; public Cons() {} }
    

    子类的构造函数如果没有明确定义它调用哪个父类的构造函数,会在执行其他任何操作之前自动调用父类中的默认构造函数。所以假设:

    class A { public A() { System.out.println("A"); } }
    

    然后这个:

    class B extends A { public B() { System.out.println("B"); } }
    

    完全等同于:

    class B extends A { public B() { super(); System.out.println("B"); } }
    

    两种情况下的输出都是:

    A
    B
    

    所以当你这样做时:

    new NoCons2();
    

    顺序是:

    1. NoCons 的默认构造函数被调用,虽然这在技术上是 (2) 的第一部分;然后
    2. 调用了 NoCons2 的默认构造函数。

    【讨论】:

      【解决方案2】:

      你想参考Java Language Specification section 12.5 Creation of New Class Instances来获取对象创建的官方规则。相关部分是:

      就在对新创建对象的引用作为结果返回之前,使用以下过程处理指示的构造函数以初始化新对象:

      1. 将构造函数的参数分配给此构造函数调用新创建的参数变量。
      2. 如果此构造函数以对同一类中的另一个构造函数的显式构造函数调用开始(使用 this),则计算参数并使用这五个相同的步骤递归地处理该构造函数调用。如果该构造函数调用突然完成,则此过程出于相同原因而突然完成;否则,继续第 5 步。
      3. 此构造函数不以显式构造函数调用同一类中的另一个构造函数开始(使用 this)。如果此构造函数用于 Object 以外的类,则此构造函数将以显式或隐式调用超类构造函数开始(使用 super)。使用这五个相同的步骤以递归方式评估超类构造函数调用的参数和过程。如果该构造函数调用突然完成,则此过程出于相同的原因突然完成。否则,请继续执行第 4 步。
      4. 执行该类的实例初始化程序和实例变量初始化程序,将实例变量初始化程序的值分配给相应的实例变量,按照它们在类源代码中以文本形式出现的从左到右的顺序。如果执行这些初始化程序中的任何一个导致异常,则不会处理更多初始化程序,并且此过程会突然完成相同的异常。否则,继续第 5 步。(在一些早期的实现中,如果字段初始化表达式是一个常量表达式,其值等于其类型的默认初始化值,编译器会错误地省略初始化字段的代码。)
      5. 执行此构造函数的其余部分。如果该执行突然完成,则此过程出于相同原因突然完成。否则,此过程正常完成。

      因此,在您的示例中,当您的类定义中没有提供构造函数时,会为您插入默认构造函数。当你写

      new NoCons2();
      
      1. 首先调用超级构造函数(为您插入对super() 的调用,因为您没有显式调用)。
      2. 正在构造的类的实例变量已初始化。
      3. 构造器主体的其余部分被执行(在你的情况下没有)。

      在您的第一个示例中,x 将在NoCons 的构造过程中设置,y 将在NoCons2 的构造过程中设置。

      因此,该示例中的确切事件顺序将类似于:

      1. 调用了 NoCons2 构造函数。
      2. 调用 super(),转到 3
      3. 调用了 NoCons 构造函数。
      4. 调用 super(),这是对 Object() 的隐式调用。
      5. Object 构造函数中发生的任何事情。
      6. x 设置为 0。
      7. 完成 NoCons 构造函数的主体,将控制权返回给 NoCons2 构造函数。
      8. y 设置为 0。
      9. 完成 NoCons2 构造函数的主体
      10. NoCons2 对象构造完成。

      【讨论】:

        【解决方案3】:

        cletus 回答了最大的问题。另一个答案是Java中的成员变量被初始化为0,null或false(取决于类型)。

        【讨论】:

          【解决方案4】:

          这基本上是调用“new”时发生的情况:

          • 内存已分配(足以容纳类的所有数据成员、所有父类以及一些内务信息)
          • 分配的内存设置为零(这意味着 0、0.0、false、null,具体取决于类型)
          • 为调用“new”之后的类调用构造函数。
          • 发生了更多事情(在下一部分之后)

          如果您不提供构造函数,编译器会执行以下操作:

          • 创建无参数构造函数
          • 创建的构造函数与类具有相同的访问权限(因此是公共的或包的)
          • super() 被调用。

          因此,当调用“new”之后的类的构造函数时,它所做的第一件事就是调用“super()”,它会调用父构造函数。这一直发生到 java.lang.Object。

          在运行构造函数的主体之前,VM 会执行以下操作:

          • 被赋值的实例变量被赋予它们
          • 然后运行实例初始化程序块(如果存在)。

          以下代码显示了所有这些:

          public class Main
          {
              private Main()
              {
              }
          
              public static void main(final String[] args)
              {
                  final Foo fooA;
                  final Foo fooB;
          
                  fooA = new Foo(7);
                  System.out.println("---------------------");
                  fooB = new Foo(42);
              }
          }
          
          class Bar
          {
              protected int valueA = getValue("valueA", 1);
              protected int valueB;
          
              static
              {
                  System.out.println("static block for Bar happens only one time");
              }
          
              {
                  System.out.println("instance block for Bar happens one time for each new Bar");
                  System.out.println("valueA = " + valueA);
                  System.out.println("valueB = " + valueB);
              }
          
              Bar()
              {
                  super();  // compiler adds this - you would not type it in
                  System.out.println("running Bar()");
                  System.out.println("valueA = " + valueA);
                  System.out.println("valueB = " + valueB);
                  valueB = getValue("valueB", 2);
              }
          
              protected static int getValue(final String name, final int val)
              {
                  System.out.println("setting " + name + " to " + val);
                  return (val);
              }
          }
          
          class Foo
              extends Bar
          {
              protected int valueC = getValue("valueC", 1);
              protected int valueD;
          
              static
              {
                  System.out.println("static block for Foo happens only one time");
              }
          
              {
                  System.out.println("instance block for Foo happens one time for each new Foo");
                  System.out.println("valueC = " + valueC);
                  System.out.println("valueD = " + valueD);
              }
          
              Foo(final int val)
              {
                  super();  // compiler adds this - you would not type it in
                  System.out.println("running Foo(int)");
                  System.out.println("valueC = " + valueC);
                  System.out.println("valueD = " + valueD);
                  valueD = getValue("valueD", val);
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-10-29
            • 2021-07-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多