78

谁能解释以下程序的输出?我认为构造函数是在实例变量之前初始化的。所以我期待输出是“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();
    }
}
4

5 回答 5

123

正确的初始化顺序是:

  1. 静态变量初始化器和静态初始化块,按文本顺序,如果该类之前没有被初始化。
  2. 构造函数中的 super() 调用,无论是显式的还是隐式的。
  3. 实例变量初始化器和实例初始化块,按文本顺序。
  4. super() 之后的构造函数的剩余部分。

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

于 2013-02-11T05:21:49.663 回答
69

如果你看一下类文件的反编译版本

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

所有实例变量都使用构造函数语句进行初始化。

于 2013-02-11T03:47:10.107 回答
4

当您调用构造函数时,实例变量初始化程序在构造函数的主体之前运行。您认为以下程序的输出是什么?

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 catched。但是,如果您尝试运行它,您会发现它没有执行任何此类操作,并且会抛出一个StackOverflowError.

于 2018-08-06T18:21:31.677 回答
2

为了澄清对静态的误解 - 我将简单地参考这一小段代码:

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 的显式和隐式调用,这将如何影响这一点,并将根据发现进行更新。它可能与其他提供的答案相似,只是他们在静态初始化时弄错了。

于 2018-02-18T03:27:23.560 回答
-6

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

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

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

3.最后调用构造函数。

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

于 2016-01-11T09:28:23.630 回答