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

时间:2012-09-03 20:32:43

标签: java constructor this super invocation

在Java教程 - Initializing Fields中,有关于实例初始化块(实例初始化器)的描述:

  

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()调用直接超类的Corresponding构造函数。

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

感谢您的回复。

3 个答案:

答案 0 :(得分:5)

编辑:事实证明,JLS毕竟是准确的,尽管它很难阅读。这一切都在section 12.5中详述:

  

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

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

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

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

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

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

  10.   

注意突出显示的部分 - 链接的构造函数被执行,然后我们跳过第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)的构造函数 not 的实例构造函数的代码已编译到其中。

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

答案 1 :(得分:0)

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

  

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

没有。 this()将调用同一个类的另一个构造函数,在本例中为默认构造函数(如果存在)。此构造函数(默认值)将调用super()。

答案 2 :(得分:0)

我认为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。执行此构造函数的其余部分......