我试图将JVM中的初始化和实例化过程拼凑在一起,但JLS对一些细节有点迟钝,所以如果有人想清除一些细节,我们将不胜感激.这是我迄今为止能够弄清楚的.
初始化
递归初始化类的静态最终变量及其作为编译时常量的接口.
退出以文本顺序处理静态块和静态字段的递归.
实例化
递归初始化作为编译时常量的类的最终实例变量.
退出递归处理文本顺序中的非静态块和实例字段,在返回时将它们预先添加到构造函数中.
好的,现在提问.
是按声明顺序处理的接口?
是在单独的递归堆栈中处理的接口?
a)如果是,是否在超类之前或之后处理接口?
b)如果是,我在推断其中一个或其他(接口或超类)在其他编译时常量之前初始化其非编译时常量字段时是正确的.
调用非默认的super()构造函数在此过程中起什么作用?
我的任何结论都错了吗?
我错过了其他任何关键细节吗?
mer*_*ike 44
区分类的初始化和对象的初始化很重要.
类初始化
首次访问时初始化类或接口,通过分配编译时常量字段,然后递归初始化超类(如果尚未初始化),然后处理静态初始化器(包括非编译时静态字段的初始化器)常量).
正如您所注意到的,类的初始化本身并不会触发它实现的接口的初始化.因此,首次访问接口时,通常通过读取不是编译时常量的字段来初始化接口.在初始化程序的评估期间可能发生此访问,从而导致递归初始化.
还值得注意的是,访问作为编译时常量的字段不会触发初始化,因为这些是在编译时计算的:
必须在编译时将对作为常量变量(§4.12.4)的字段的引用解析为由常量变量的初始值设定项表示的值V.
如果这样的字段是静态的,那么在二进制文件的代码中不应该存在对该字段的引用,包括声明该字段的类或接口.这样的字段必须总是看似已经初始化(§12.4.2); 必须永远不要观察该字段的默认初始值(如果不同于V).
如果这样的字段是非静态的,那么除了包含该字段的类之外,二进制文件中的代码中不应该存在对该字段的引用.(它将是一个类而不是一个接口,因为一个接口只有静态字段.)该类应该有代码在实例创建期间将字段的值设置为V(第12.5节).
对象初始化
每当创建新对象时,通常通过评估类实例创建表达式来初始化对象.其过程如下:
将构造函数的参数分配给此构造函数调用的新创建的参数变量.
如果此构造函数以同一个类中的另一个构造函数的显式构造函数调用(第8.8.7.1节)开头(使用此方法),则使用这五个相同步骤计算参数并以递归方式处理该构造函数调用.如果该构造函数调用突然完成,则此过程突然完成,原因相同; 否则,继续步骤5.
此构造函数不以同一个类中的另一个构造函数的显式构造函数调用开头(使用此方法).如果此构造函数用于Object以外的类,则此构造函数将以超类构造函数的显式或隐式调用开始(使用super).使用这五个相同的步骤评估参数并递归处理超类构造函数调用.如果该构造函数调用突然完成,则此过程突然完成,原因相同.否则,继续执行步骤4.
为此类执行实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按从左到右的顺序分配给相应的实例变量,在这些顺序中,它们以文本方式出现在类的源代码中.如果执行任何这些初始值设定项导致异常,则不会处理其他初始化程序,并且此过程会突然完成同样的异常.否则,继续步骤5.
执行此构造函数的其余部分.如果执行突然完成,则此过程突然完成,原因相同.否则,此过程正常完成.
正如我们在第3步中看到的,对超级构造函数的显式调用的存在只是改变了调用哪个超类构造函数.
以下是在对象创建期间打印每个步骤的顺序的示例。
InstanceCreateStepTest.java:
import javax.annotation.PostConstruct;
/**
* Test steps of instance creation.
*
* @author eric
* @date Jan 7, 2018 3:31:12 AM
*/
public class InstanceCreateStepTest {
public static void main(String[] args) {
new Sub().hello();
System.out.printf("%s\n", "------------");
new Sub().hello();
}
}
class Base {
static {
System.out.printf("%s - %s - %s\n", "base", "static", "block");
}
{
System.out.printf("%s - %s - %s\n", "base", "instance", "block");
}
public Base() {
System.out.printf("%s - %s\n", "base", "constructor");
}
@PostConstruct
public void init() {
System.out.printf("%s - %s\n", "base", "PostConstruct");
}
public void hello() {
System.out.printf("%s - %s\n", "base", "method");
}
}
class Sub extends Base {
static {
System.out.printf("%s - %s - %s\n", "sub", "static", "block");
}
{
System.out.printf("%s - %s - %s\n", "sub", "instance", "block");
}
public Sub() {
System.out.printf("%s - %s\n", "sub", "constructor");
}
@PostConstruct
public void init() {
System.out.printf("%s - %s\n", "sub", "PostConstruct");
}
@Override
public void hello() {
// super.hello();
System.out.printf("%s - %s\n", "sub", "method");
}
}
Run Code Online (Sandbox Code Playgroud)
执行:
只需调用 main 方法,然后检查输出。
提示:
@PostConstruct不会被调用,除非您在某个容器中调用它,例如Spring-boot,因为它依赖于这些容器来实现注释,例如@PostConstruct。| 归档时间: |
|
| 查看次数: |
22817 次 |
| 最近记录: |