想象一个Java类,它具有您可以在类中找到的大多数功能。例如:它继承自另一个类,实现了几个接口,包括一些'静态最终'常量,一些最终常量,一些静态变量,实例变量,一个静态块,一个未命名的代码块(只是{}中的代码),构造函数,方法等。
当有问题的类第一次加载到JVM中时,类的各个部分以什么顺序初始化或加载到JVM中? JVM中的调用堆栈对于加载是什么样的?假设只有一个类加载器在这里工作。
这可以追溯到Java的绝对基础/内部,但我找不到一篇解释正确序列的好文章。
答案 0 :(得分:4)
2.17.4初始化
类的初始化包括:
- 执行静态初始值设定项(§2.11)和
- 在类中声明的静态字段的初始化程序(第2.9.2节)。
接口的初始化包括为接口中声明的字段执行初始化器(第2.13.3.1节)。
在初始化类或接口之前,必须初始化其直接超类,但不需要初始化类实现的接口。同样,在初始化接口之前,不需要初始化接口的超接口。
类或接口类型T将在下列之一发生之前立即初始化:
- T是一个类,创建了一个T实例。
- T是一个类,调用T的静态方法。
- 使用或分配T的非常量静态字段。常量字段是(显式地或隐式地)最终字段和静态字段,并且使用编译时常量表达式的值初始化。必须在编译时将对此类字段的引用解析为编译时常量值的副本,因此使用此类字段永远不会导致初始化。
调用库类中的某些方法(第3.12节)也会导致类或接口初始化。有关详细信息,请参阅Java 2平台的类库规范(例如,类Class和包java.lang.reflect)。
这里的意图是一个类型有一组初始化器,它们使它处于一致状态,并且该状态是其他类观察到的第一个状态。静态初始值设定项和类变量初始值设定项以文本顺序执行,并且可能不引用在声明在使用后以文本方式出现的类中声明的类变量,即使这些类变量在范围内。此限制旨在在编译时检测大多数循环或其他格式错误的初始化。
在初始化类或接口之前,如果以前没有初始化超类,则初始化它的超类。
Initialization in JVMS 8 is in Chapter 5.5
的更新版本类或接口的初始化包括执行其类或接口初始化方法(§2.9)。
只有在以下情况下才能初始化类或接口:
- 执行引用类或接口的
new
,getstatic
,putstatic
或invokestatic
中的任何一个(§new
,§getstatic
,§putstatic
,§invokestatic
) 所有这些指令都通过字段引用或方法引用直接或间接引用类 执行新指令后,如果尚未初始化引用的类或接口,则初始化它 执行getstatic
,putstatic
或invokestatic
指令后,如果尚未初始化,则声明已解析字段或方法的类或接口已初始化。java.lang.invoke.MethodHandle
实例的第一次调用,它是Java虚拟机(§5.4.3.5)解析方法句柄的结果,其类型为2(REF_getStatic
) ,4(REF_putStatic
),6(REF_invokeStatic
)或8(REF_newInvokeSpecial
)。- 在类库(§2.12)中调用某些反射方法,例如,在课程
Class
或包java.lang.reflect
中。- 其子类之一的初始化。
- 它被指定为Java虚拟机启动时的初始类(§5.2)。
在初始化之前,必须链接一个类或接口,即验证,准备并可选择解析。
由于Java虚拟机是多线程的,因此初始化类或接口需要仔细同步,因为其他一些线程可能正在尝试同时初始化相同的类或接口。
作为该类或接口的初始化的一部分,还可以递归地请求类或接口的初始化。Java虚拟机的实现负责通过使用以下过程来处理同步和递归初始化 它假定
Class
对象已经过验证和准备,并且Class
对象包含指示以下四种情况之一的状态:
- 此
Class
对象已经过验证和准备但尚未初始化。- 此
Class
对象正由某个特定线程初始化。- 此
Class
对象已完全初始化并可供使用。- 此
Class
对象处于错误状态,可能是因为初始化尝试失败。
答案 1 :(得分:1)
JLS怎么样,特别是第12.4节?