在使用Java运行构造函数代码之前是否初始化了字段?

时间:2013-02-11 03:41:22

标签: java constructor initialization

任何人都可以解释以下程序的输出吗?我认为构造函数在实例变量之前被初始化。所以我期待输出为“XZYY”。

class X {
    Y b = new Y();

    X() {
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {
    Y y = new Y();

    Z() {
        System.out.print("Z");
    }

    public static void main(String[] args) {
        new Z();
    }
}

5 个答案:

答案 0 :(得分:87)

正确的初始化顺序是:

  1. 静态变量初始化和静态初始化块,按文本顺序排列,如果该类之前尚未初始化。
  2. 构造函数中的super()调用,无论是显式还是隐式。
  3. 实例变量初始化程序和实例初始化程序段,按文本顺序排列。
  4. super()之后的剩余构造函数体。
  5. 请参阅§2.17.5-6 of the Java Virtual Machine Specification部分。

答案 1 :(得分:55)

如果查看类文件的反编译版本

class X {
    Y b;

    X() {
        b = new Y();
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {

    Y y;

    Z() {
        y = new Y();
        System.out.print("Z");
    }

    public static void main(String args[]) {
        new Z();
    }
}

您可以发现实例变量y在构造函数中移动,因此执行顺序如下

  1. 调用Z
  2. 的构造函数
  3. 它会触发X
  4. 的默认构造函数
  5. 调用X构造函数new Y()的第一行。
  6. 打印Y
  7. 打印X
  8. 在构造函数Z new Y()
  9. 中调用第一行
  10. 打印Y
  11. 打印Z
  12. 使用构造函数语句初始化所有实例变量。

答案 2 :(得分:1)

澄清对静态的误解 - 我将简单地引用这一小段代码:

public class Foo {
  { System.out.println("Instance Block 1"); }
  static { System.out.println("Static Block 1"); }
  public static final Foo FOO = new Foo();
  { System.out.println("Instance Block 2"); }
  static { System.out.println("Static Block 2 (Weird!!)"); }
  public Foo() { System.out.println("Constructor"); }
  static public void main(String p[]) {
    System.out.println("In Main");
    new Foo();
  }
}

惊喜是输出如下:

Static Block 1
Instance Block 1
Instance Block 2
Constructor
Static Block 2 (Weird!!)
In Main
Instance Block 1
Instance Block 2
Constructor

请注意,我们在两个实例static {}后面有一个名为{}。这是因为我们在中间抛出构造函数,在第一次调用构造函数时插入执行顺序。

当我处理这个答案时发现了这个问题 - https://stackoverflow.com/a/30837385/744133

基本上我们观察到这种情况发生:

  1. 第一次初始化对象时 根据发生的顺序初始化静态和实例初始化的当前对象

  2. 对于所有下一次初始化,仅按发生顺序进行实例初始化,因为已经发生了静态初始化。

  3. 我需要研究继承的混合,以及对super的显式和隐式调用以及这将如何影响这一点,并将更新结果。它可能与其他提供的答案类似,只是它们在静态初始化时出错了。

答案 3 :(得分:1)

调用构造函数时,实例变量初始化程序在构造函数的主体之前运行。您如何看待以下程序的输出?

public class Tester {
    private Tester internalInstance = new Tester();
    public Tester() throws Exception {
        throw new Exception("Boom");
    }
    public static void main(String[] args) {
        try {
            Tester b = new Tester();
            System.out.println("Eye-Opener!");
        } catch (Exception ex) {
            System.out.println("Exception catched");
        }
    }
}

main方法调用Tester构造函数,该构造函数引发异常。您可能希望catch子句捕获此异常并显示捕获到的异常。 但是,如果您尝试运行它, 发现它没有执行任何操作,并且抛出了StackOverflowError

答案 4 :(得分:-5)

初始化序列在JLS 12.5中指定:

1.首先,为新对象分配内存

2.然后,对象中的所有实例变量(包括在此类中定义的变量及其所有超类)都被初始化为其默认值

3.最后,调用构造函数。

https://stackoverflow.com/questions/26552799/which-run-first-default-values-for-instance-variables-or-super-constructors