有人能解释一下以下程序的输出吗?我认为构造函数是在实例变量之前初始化的。所以我希望输出是"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();
}
}

tl;dr:类成员初始化中的new被隐式地放在构造函数的顶部。

正确的初始化顺序是:

如果类以前没有初始化过,静态变量初始化器和静态初始化块将按文本顺序进行初始化。

在构造函数中调用super(),无论是显式的还是隐式的。

实例变量初始化器和实例初始化块,按文本顺序。

super()后面的构造函数的剩余主体。

参见JavaVirtualMealEngress规范的第2.2.5-6节。

链接到Java 8的JLS:DOCS.Oracle .COM/JavaS/SCOS/JLS/SE8/HTML/JLS-12. HTML JJLS-12‌和γ8203;5。

也错了-看到我对同样问题的@&211;scarl&243;pez的评论。注意,static并不是您所指的文档部分的一部分。

另一篇有评论的文章已经被删除了,但我在一个补充答案中提供了一些细节和示例代码。不是一个完整的答案,但至少强调了static {}的情况。

@Yoyo此答案与JLS一致:具体而言,关于您在答案中提到的执行,请参见&167;12.4.2第9项。如果您的实现不符合JLS,则将其报告为bug。

混淆的是类和实例的实例化是分开讨论的。我想这会让实现细节在如何混合这两个方面保持开放。

值得指出的是,步骤3中的"按纹理顺序"适用于实例初始化块(IIB)和实例变量初始化器(IVB)的交错顺序,这意味着可以有IIB1、IVB1、IIB2、IVB2等执行顺序。

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

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在构造函数中被移动,所以执行顺序如下

调用EDOCX1[1]的构造函数

触发X的默认构造函数

调用X构造函数new Y()的第一行。

打印Y

打印X

调用构造函数z new Y()中的第一行

打印y。

打印Z

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

很好的解释@arun p johny+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");
}
}
}

主方法调用测试人员构造函数,这会引发异常。您可能期望catch子句捕获此异常并打印捕获的异常。但是如果你试着运行它,你发现它什么也不做,就扔了一个StackOverflowError。

因此,您基本上是想表明,在控件进入throw部分之前,构造函数将继续调用自己(因为private tester internalinstance=new tester();)。我的理解正确吗?

为了澄清静态的误解,我将简单地引用这段代码:

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。

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

在第一次初始化对象时,初始化静态和实例初始化的当前对象,根据发生顺序混合初始化

对于所有接下来的初始化,只按照发生顺序进行实例初始化,因为静态初始化已经发生。

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

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

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

2.然后对象中的所有实例变量(包括这个类中定义的变量及其所有超类)都初始化为它们的默认值。

3.最后调用构造函数。