static
变量和初始值设定项块按照它们在源代码中出现的顺序进行初始化(在非最终变量之前初始化的变量除外)。static
static final
static
s1
在 之前初始化,因此 对 的调用返回默认值 ,即 。s2
getVal()
s2
null
您可以更改变量的顺序,以便首先初始化:static
s2
static String s2 = "S2";
static String s1 = getVal();
另一种强制初始化发生在 之前发生的方法是使 final:s2
s1
s2
static String s1 = getVal();
static final String s2 = "S2";
以下是JLS 12.4.2中所述的初始化顺序的摘录。详细的初始化过程。突出显示与变量相关的部分。static
对于每个类或接口 C,都有一个唯一的初始化锁 LC。从 C 到 LC 的映射由 Java 虚拟机实现自行决定。然后,初始化 C 的过程如下:
-
在 C 的初始化锁 LC 上同步。这涉及等到当前线程可以获取LC。
-
如果 C 的 Class 对象指示某个其他线程正在对 C 进行初始化,则释放 LC 并阻止当前线程,直到通知正在进行的初始化已完成,此时重复此步骤。
-
如果 C 的 Class 对象指示当前线程正在对 C 进行初始化,则这必须是初始化的递归请求。释放 LC 并正常完成。
-
如果 C 的 Class 对象指示 C 已初始化,则不需要进一步的操作。释放 LC 并正常完成。
-
如果 C 的 Class 对象处于错误状态,则无法进行初始化。释放 LC 并抛出一个 NoClassDefFoundError。
-
否则,请记录当前线程正在初始化 C 的 Class 对象的事实,并释放 LC。
然后,初始化 C 的静态字段,这些字段是常量变量(§4.12.4, §8.3.2, §9.3.1)。
-
接下来,如果 C 是一个类而不是一个接口,那么让 SC 成为它的超类,让 SI1, ..., SIn 是 C 的所有超接口,它们至少声明一个默认方法。超接口的顺序由对 C 直接实现的每个接口的超接口层次结构的递归枚举给出(按 C 的 implements 子句的从左到右的顺序)。对于我直接由 C 实现的每个接口,枚举在返回 I 之前在 I 的超接口上递归(以 I 的 extends 子句的从左到右的顺序)。
对于列表 [ SC, SI1, ..., SIn ] 中的每个 S,如果 S 尚未初始化,则以递归方式对 S 执行整个过程。如有必要,请先验证并准备 S。
如果 S 的初始化由于引发的异常而突然完成,则获取 LC,将 C 的 Class 对象标记为错误,通知所有等待线程,释放 LC,然后突然完成,引发初始化 S 时导致的相同异常。
-
接下来,通过查询 C 的定义类装入器来确定是否为 C 启用了断言 (§14.10)。
-
接下来,按文本顺序执行类的类变量初始值设定项和静态初始值设定项,或接口的字段初始值设定项,就好像它们是单个块一样。