Java显式构造函数调用和实例初始化程序

Wua*_*ner 4 java constructor this super invocation

在Java教程 - 初始化字段中,有关于实例初始化块(实例初始化程序)的描述:

Java编译器将初始化程序块复制到每个构造函数中.因此,该方法可用于在多个构造函数之间共享代码块.

如果释义正确,则对于以下代码:

public class ConstructorTest {

    public static void main(String[] args) {
        Child c = new Child();
    }
}

class Parent {
    Parent() {
        System.out.println("Parent non-argument Constructor");
    }
}

class Child extends Parent {

    {
        System.out.println("Child Instance Initialization Block");
    }

    Child() {
        this(2);
        System.out.println("Child no-argument Constructor");

    }

    Child(int i) {
        this(10, i);
        System.out.println("Child 1-argument Constructor");
    }

    Child(int i, int j) {
        System.out.println("Child 2-argument Constructor");
    }
}
Run Code Online (Sandbox Code Playgroud)

输出应该是:

Parent non-parm Constructor
Child Instance Initialization Block
Child 2-argument Constructor
Child Instance Initialization Block
Child 1-argument Constructor
Child Instance Initialization Block
Child no-argument Constructor
Run Code Online (Sandbox Code Playgroud)

但实际输出是:

Parent non-argument Constructor
Child Instance Initialization Block
Child 2-argument Constructor
Child 1-argument Constructor
Child no-argument Constructor
Run Code Online (Sandbox Code Playgroud)

我被误解了那句话的意思,还是描述不够准确?

关于显式构造函数调用的另一个疑问:

基于两个基础知识:

  • 如果存在,则另一个构造函数的调用必须是构造函数中的第一行.
  • 在构造函数中,使用this()调用另一个构造函数,使用super()调用直接超类的Corresponding构造函数.

那个MEANS在子类的构造函数中使用this()会隐式删除对超类的无参数构造函数的默认调用吗?

谢谢回复.

Jon*_*eet 5

编辑:事实证明,JLS毕竟是准确的,虽然它很难阅读.这一切详见12.5节:

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

  1. 将构造函数的参数分配给此构造函数调用的新创建的参数变量.

  2. 如果此构造函数以同一个类中的另一个构造函数的显式构造函数调用(第8.8.7.1节)开头(使用此方法),则使用这五个相同步骤计算参数并以递归方式处理该构造函数调用.如果该构造函数调用突然完成,则此过程突然完成,原因相同; 否则,继续步骤5.

  3. 此构造函数不以同一个类中的另一个构造函数的显式构造函数调用开头(使用此方法).如果此构造函数用于Object以外的类,则此构造函数将以超类构造函数的显式或隐式调用开始(使用super).使用这五个相同的步骤评估参数并递归处理超类构造函数调用.如果该构造函数调用突然完成,则此过程突然完成,原因相同.否则,继续执行步骤4.

  4. 为此类执行实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按从左到右的顺序分配给相应的实例变量,在这些顺序中,它们以文本方式出现在类的源代码中.如果执行任何这些初始值设定项导致异常,则不会处理其他初始化程序,并且此过程会突然完成同样的异常.否则,继续步骤5.

  5. 执行此构造函数的其余部分.如果执行突然完成,则此过程突然完成,原因相同.否则,此过程正常完成.

注意突出显示的部分 - 链接的构造函数被执行,然后我们跳过步骤4,它将执行实例初始化程序.

实际情况是,实例和字段初始化程序只执行一次,您可以从输出中判断出来.

非正式地,我认为将程序描述为:

  • 继续将构造函数链接在同一个类(this(...))中,直到到达不以其开头的构造函数体this.
  • 执行适当的超级构造函数
  • 执行实例变量初始值设定项和实例初始值设定项
  • 执行"innermost"构造函数的主体
  • 继续弹出构造函数体的堆栈,直到最终得到"entry"构造函数

那个MEANS在子类的构造函数中使用this()会隐式删除对超类的无参数构造函数的默认调用吗?

是.在某处,构造函数链的类,你保证与调用构造函数来结束super或隐或显式.这是唯一被调用的超类构造函数.

编辑:请注意,您引用的教程明显不正确.

样本类:

public class Test {
    {
        System.out.println("Foo");
    }
    
    public Test() {
    }
    
    public Test(int i)  {
        this();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出来自javap -c:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1  // Method java/lang/Object."<init>": ()V
       4: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #3  // String Foo
       9: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: return

  public Test(int);
    Code:
       0: aload_0
       1: invokespecial #5                  // Method "<init>":()V
       4: return
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,对于构造Test(int)没有对编译到它的构造函数的代码.

基本上,只有直接调用超类构造函数的构造函数才会将实例初始化代码复制到其中.所有其他构造函数最终将导致实例初始化程序代码通过调用超类构造函数的构造函数执行,当然.