繁体   English   中英

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

[英]Java explicit constructor invocation & Instance Initializer

在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");
    }
}

输出应为:

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

但实际输出为:

Parent non-argument Constructor
Child Instance Initialization Block
Child 2-argument Constructor
Child 1-argument Constructor
Child no-argument Constructor

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

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

基于两个基础:

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

在子类的构造函数中使用MEANS是否使用this()将隐式删除对超类的无参数构造函数的默认调用?

谢谢回复。

编辑:事实证明,尽管很难阅读,但JLS毕竟是准确的。 12.5节中有详细介绍

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

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

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

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

  4. 执行此类的实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按从左到右的顺序在文本中显示在该类的源代码中,然后将其分配给相应的实例变量。 如果执行这些初始化程序中的任何一个导致异常,则不会再处理其他初始化程序,并且该过程会因相同的异常而突然完成。 否则,请继续执行步骤5。

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

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

现实情况是,实例和字段初始化器仅执行一次,正如您从输出中可以看到的那样。

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

  • 保持链接构造函数在同一类( this(...) )内,直到到达不是以this开头的构造函数主体。
  • 执行适当的超级构造函数
  • 执行实例变量初始化程序和实例初始化程序
  • 执行“最内层”构造函数的主体
  • 不断弹出构造函数主体的堆栈,直到获得“ entry”构造函数为止

在子类的构造函数中使用MEANS是否使用this()将隐式删除对超类的无参数构造函数的默认调用?

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

编辑:请注意,您引用的教程显然是不正确的。

范例类别:

public class Test {
    {
        System.out.println("Foo");
    }
    
    public Test() {
    }
    
    public Test(int i)  {
        this();
    }
}

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
}

如您所见, Test(int)的构造函数没有将实例构造函数的代码编译到其中。

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

实例化类的对象时,初始化块仅运行一次(使用任何构造函数<=,这是您的疑问)。 当类加载器加载类时,静态初始化块仅运行一次。

在子类的构造函数中使用MEANS是否使用this()将隐式删除对超类的无参数构造函数的默认调用?

否。this()将调用同一类的另一个构造函数,在这种情况下为默认构造函数(如果存在)。 此构造函数(默认构造函数)将调用super()。

我认为Java语言规范更为准确: http : //docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6

创建类的实例时,将执行在类中声明的实例初始化器

并在http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5步骤4和5中描述了您观察到的输出:

4。 执行此类的实例初始值设定项和实例变量初始值设定项...

5。 执行此构造函数的其余部分...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM