我正在尝试将 JVM 中的初始化和实例化过程拼凑在一起,但 JLS 在一些细节上有点迟钝,因此如果有人介意清理一些细节,我们将不胜感激。这是我到目前为止所能弄清楚的。
初始化
递归地初始化类的静态最终变量及其作为编译时常量的接口。
按文本顺序退出递归处理静态块和静态字段。
实例化
递归地初始化作为编译时常量的类的最终实例变量。
-
返回按文本顺序处理非静态块和实例字段的递归,在返回时将它们添加到构造函数中。
好的,现在提问。
接口是按照声明的顺序处理的吗?
-
接口是否在单独的递归堆栈中处理?
a) 如果是,接口是在超类之前还是之后处理?
b)如果是,我是否正确地推断出一个或其他(接口或超类)在其他编译时常量之前初始化其非编译时常量字段。
在此过程中,对非默认 super() 构造函数的调用起什么作用?
我的结论是否有误?
我是否遗漏了其他关键细节?
区分类的初始化和对象的初始化很重要。
类初始化
类或接口初始化于首次访问,通过分配编译时常量字段,然后递归地初始化超类(如果尚未初始化),然后处理静态初始化程序(其中包括不是编译时常量的静态字段的初始化程序)。
正如您所注意到的,类的初始化本身不会触发其实现的接口的初始化。因此,接口在首次访问时会被初始化,通常是通过读取不是编译时常量的字段来初始化。此访问可能发生在初始化程序的求值期间,从而导致递归初始化。
还值得注意的是,初始化不是通过访问编译时常量字段来触发的,因为这些字段是在编译时间:
对常量变量(第 4.12.4 节)字段的引用必须在编译时解析为由常量变量的初始值设定项表示的值 V。
如果这样的字段是静态的,则二进制文件的代码中不应存在对该字段的引用,包括声明该字段的类或接口。这样的字段必须始终显示为已初始化(第 12.4.2 节);决不能遵守该字段的默认初始值(如果与 V 不同)。
如果这样的字段是非静态的,则二进制文件的代码中不应存在对该字段的引用,包含该字段的类除外。 (它将是一个类而不是一个接口,因为接口仅具有静态字段。)该类应该具有在实例创建期间将字段的值设置为 V 的代码(第 12.5 节)。
对象初始化
一个对象被初始化每当创建一个新对象时,通常通过评估类实例创建表达式来实现。其过程如下:
-
将构造函数的参数分配给为此构造函数调用新创建的参数变量。
-
如果此构造函数以同一类中另一个构造函数(使用 this)的显式构造函数调用(第 8.8.7.1 节)开始,则评估参数并使用这相同的五个步骤递归地处理该构造函数调用。如果该构造函数调用突然完成,则此过程也会出于同样的原因突然完成;否则,继续步骤 5。
-
此构造函数不会以显式构造函数调用同一类中的另一个构造函数(使用 this)开始。如果此构造函数用于除 Object 之外的类,则此构造函数将以显式或隐式调用超类构造函数(使用 super)开始。使用这五个步骤递归地评估参数并处理超类构造函数调用。如果该构造函数调用突然完成,则此过程也会出于同样的原因突然完成。否则,继续步骤 4。
-
执行此类的实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按照类源代码中文本出现的从左到右的顺序分配给相应的实例变量。如果执行任何这些初始化程序导致异常,则不会再处理其他初始化程序,并且此过程会突然完成并出现相同的异常。否则,继续步骤 5。
-
执行该构造函数主体的其余部分。如果该执行突然完成,则该过程也会出于同样的原因突然完成。否则,该过程将正常完成。
正如我们在步骤 3 中看到的,对超级构造函数的显式调用的存在只会改变调用哪个超类构造函数。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)