java运行 构造函数_在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();

}

}

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.最后调用构造函数。

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

问题不在于默认值。

所有对规范的错误解释。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值