任何人都可以解释以下程序的输出吗?我认为构造函数在实例变量之前被初始化。所以我期待输出为“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();
}
}
答案 0 :(得分:87)
正确的初始化顺序是:
答案 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
在构造函数中移动,因此执行顺序如下
Z
X
X
构造函数new Y()
的第一行。new Y()
Y
使用构造函数语句初始化所有实例变量。
答案 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。
基本上我们观察到这种情况发生:
第一次初始化对象时 根据发生的顺序初始化静态和实例初始化的当前对象
对于所有下一次初始化,仅按发生顺序进行实例初始化,因为已经发生了静态初始化。
我需要研究继承的混合,以及对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.最后,调用构造函数。