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

谁能解释以下程序的输出? 我认为构造函数是在实例变量之前初始化的。 因此,我期望输出为“ 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();
    }
}
Praveen Kumar asked 2019-10-09T10:49:42Z
5个解决方案
87 votes

正确的初始化顺序是:

  1. 如果该类先前尚未初始化,则按文本顺序的静态变量初始化程序和静态初始化块。
  2. 构造函数中的super()调用,无论是显式的还是隐式的。
  3. 实例变量初始化程序和实例初始化块,按文本顺序。
  4. super()之后构造函数的其余主体。

请参阅《 Java虚拟机规范》第2.17.5-6节。

user207421 answered 2019-10-09T10:50:24Z
55 votes

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

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. 调用Y的构造函数
  2. 它触发Y的默认构造函数
  3. 调用了Y构造函数new Y()的第一行。
  4. 列印Y
  5. 列印X
  6. 调用构造函数Z中的第一行Y
  7. 打印Y
  8. 列印Z

通过使用构造函数语句初始化所有实例变量。

Arun P Johny answered 2019-10-09T10:51:55Z
1 votes

为了澄清对静态的误解-我将简单地引用以下小段代码:

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. 对于所有后续初始化,仅按发生顺序进行实例初始化,因为静态初始化已经发生。

我需要研究继承的混合,以及对super的显式和隐式调用的混合,这将如何影响这种情况,并会根据发现进行更新。 它可能与其他提供的答案类似,不同之处在于它们使用静态初始化弄错了。

YoYo answered 2019-10-09T10:53:05Z
1 votes

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

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子句捕获此异常并打印出已捕获的Exception。但是,如果您尝试运行它,发现它不执行任何操作,并抛出StackOverflowError

Vivek answered 2019-10-09T10:53:37Z
-5 votes

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

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

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

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

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

Abdul Razak answered 2019-10-09T10:54:28Z
translate from https://stackoverflow.com:/questions/14805547/are-fields-initialized-before-constructor-code-is-run-in-java