JAVA初始化块
正如一些来源所说,Java实例初始化块在创建实例时或在构造函数之前执行。但想象一下这种情况:JAVA初始化块,java,Java,正如一些来源所说,Java实例初始化块在创建实例时或在构造函数之前执行。但想象一下这种情况: public class Foo { { System.out.println("Foo init"); } public Foo() { { System.out.println("Foo constr"); } } } public class Main extends Foo {
public class Foo {
{
System.out.println("Foo init");
}
public Foo()
{
{
System.out.println("Foo constr");
}
}
}
public class Main extends Foo {
{
System.out.println("Main init");
}
public Main()
{
{
System.out.println("Main constr");
}
}
public static void main(String[] args) {
new Main();
}
}
输出如预测:
Foo init
Foo constr
Main init
Main constr
Foo init
Foo constr
Main init
Main constr
所以我的问题是-什么是实例初始化块的正确定义,因为它显然不是在构造函数之前执行的,因为输出应该是
Main init
Foo init
Foo constr
Main constr
因为主构造函数是在调用super之前调用的,主初始化块应该是第一个 否。初始化块直接复制到构造函数中。显然,这里面也隐含着一种超能力。所以你的例子变成了
public class Foo {
public Foo()
{
{System.out.println("Foo init");} // initializer.
{System.out.println("Foo constr");}
}
}
public class Main extends Foo {
public Main()
{
super(); // super constructor.
{System.out.println("Main init");} // initializer.
{System.out.println("Main constr");}
}
public static void main(String[] args) {
new Main();
}
}
这就解释了你观察到的
否。初始化块直接复制到构造函数中。显然,这里面也隐含着一种超能力。所以你的例子变成了
public class Foo {
public Foo()
{
{System.out.println("Foo init");} // initializer.
{System.out.println("Foo constr");}
}
}
public class Main extends Foo {
public Main()
{
super(); // super constructor.
{System.out.println("Main init");} // initializer.
{System.out.println("Main constr");}
}
public static void main(String[] args) {
new Main();
}
}
这就解释了你观察到的
上述状态将首先转到Main。但在执行任何主要操作之前,都会调用super。所以,Foo被称为。现在,在Foo中进行检查以查看实例初始化块。所以,你得到了。Foo init。接下来执行Foo中的语句。所以,你得到了-Foo constr。接下来,控件返回到Main,Main现在检查Main类是否存在初始化块。所以,主初始化被打印。。然后,打印Main的其他语句。这个定义是正确的。。谅解。。好取决于你怎么看
上述状态将首先转到Main。但在执行任何主要操作之前,都会调用super。所以,Foo被称为。现在,在Foo中进行检查以查看实例初始化块。所以,你得到了。Foo init。接下来执行Foo中的语句。所以,你得到了-Foo constr。接下来,控件返回到Main,Main现在检查Main类是否存在初始化块。所以,主初始化被打印。。然后,打印Main的其他语句。这个定义是正确的。。谅解。。好取决于你怎么看…最后一句话不正确。Main在Foo之后执行,因为类构造函数是在其超级构造函数完成之后执行的。
有关更多详细信息,请参见哪个处理隐式和显式超级构造函数调用。最后一条语句不正确。Main在Foo之后执行,因为类构造函数是在其超级构造函数完成之后执行的。
有关更多详细信息,请参见哪个处理隐式和显式超级构造函数调用。编译器会将代码转换为如下内容:
public class Main extends Foo {
void _init()
{System.out.println("Main init");}
public Main()
{
super();
_init();
{System.out.println("Main constr");}
}
}
主要规则是:
在当前类的任何初始化代码之前调用super
在构造函数主体之前调用初始值设定项块
编译器会将代码转换为如下内容:
public class Main extends Foo {
void _init()
{System.out.println("Main init");}
public Main()
{
super();
_init();
{System.out.println("Main constr");}
}
}
主要规则是:
在当前类的任何初始化代码之前调用super
在构造函数主体之前调用初始值设定项块
JLS中有一个解释,但让我提炼出最关键的部分:
初始化类的过程取决于JVM的实现。
如果超类尚未初始化,则在其子类步骤7之前初始化该超类。
评估所有静态初始值设定项和字段,就像它们是文本顺序中的单个块一样。最后一部分很重要;这意味着首先看到的是先初始化的。
这就是为什么您会看到这样的行为-因为Main是Foo的子类,它还没有初始化,所以它的静态块在那一刻没有被计算
因此,Main的构造函数直到Foo的构造函数之后才执行,因为子类中有。JLS中有一个解释,但让我提取最关键的部分:
初始化类的过程取决于JVM的实现。
如果超类尚未初始化,则在其子类步骤7之前初始化该超类。
评估所有静态初始值设定项和字段,就像它们是文本顺序中的单个块一样。最后一部分很重要;这意味着首先看到的是先初始化的。
这就是为什么您会看到这样的行为-因为Main是Foo的子类,它还没有初始化,所以它的静态块在那一刻没有被计算
因此,Main的构造函数直到Foo的构造函数之后才执行,因为子类中有。请参见此代码的内部工作方式:
class Foo {
{System.out.println("Foo init");}
public Foo()
{
{System.out.println("Foo constr");}
}
}
class Main extends Foo {
{System.out.println("Main init");}
public Main()
{
{System.out.println("Main constr");}
}
public static void main(String[] args) {
new Main();
}
}
步骤1:JVM调用main类的main方法
第2步:构造函数Main有内部super,如果您不使用它,它由JVM提供,因此super将调用super类构造函数,即super类的Foo
步骤3:现在,在调用超类的Foo之前,JVM将检查是否存在任何IIB,即实例初始化块,因此,在打印Foo constr之前,将先打印Foo init
目前为止的输出为:
Foo init
Foo constr
步骤4:我们的控件返回到当前构造函数,然后在
e执行当前Constructor JVM将调用IIB,即实例初始化块,并将打印Main init。最后是主施工图
因此,最后的结论是:
Foo init
Foo constr
Main init
Main constr
实际上,任何构造函数的第一次调用总是Super或this。
您正在使用new Main创建新对象;当然,这将调用构造函数,构造函数总是被调用来初始化对象。查看此代码在内部的工作方式:
class Foo {
{System.out.println("Foo init");}
public Foo()
{
{System.out.println("Foo constr");}
}
}
class Main extends Foo {
{System.out.println("Main init");}
public Main()
{
{System.out.println("Main constr");}
}
public static void main(String[] args) {
new Main();
}
}
步骤1:JVM调用main类的main方法
第2步:构造函数Main有内部super,如果您不使用它,它由JVM提供,因此super将调用super类构造函数,即super类的Foo
步骤3:现在,在调用超类的Foo之前,JVM将检查是否存在任何IIB,即实例初始化块,因此,在打印Foo constr之前,将先打印Foo init
目前为止的输出为:
Foo init
Foo constr
第4步:我们的控制返回到当前构造函数,在执行之前,当前构造函数JVM将调用IIB,即实例初始化块,并将打印Main init。最后是主施工图
因此,最后的结论是:
Foo init
Foo constr
Main init
Main constr
实际上,任何构造函数的第一次调用总是Super或this。
您正在使用new Main创建新对象;当然,这将调用构造函数,构造函数总是被调用来初始化对象。您可以看到仅使用Static with instance initialization块在输出中的差异。您可以看到仅使用Static with instance initialization块在输出中的差异。