一尘不染

Java初始化和实例化顺序

java

我试图将JVM中的初始化和实例化过程组合在一起,但是JLS在一些细节上有点笨拙,因此,如果有人介意清理一些细节,将不胜感激。到目前为止,这是我能够弄清楚的。

初始化

递归初始化该类及其接口的静态最终变量,这些变量是编译时间常数。

从递归中退出,按文本顺序处理静态块和静态字段。

实例化

  1. 递归初始化作为编译时间常数的类的最终实例变量。
  2. 按文本顺序退出递归处理非静态块和实例字段,并在返回时将它们放在构造函数之前。

好的,现在提出问题。

  1. 接口是否按声明顺序处理?
  2. 接口是在单独的递归堆栈中处理的吗?

a)如果是,接口是在超类之前还是之后进行处理的?
b)如果是,我是否正确推断一个或另一个(接口或超类)在其他编译时常数之前初始化了其非编译时常数字段。

  1. 在此过程中,对非默认super()构造函数的调用起什么作用?
  2. 我的结论有误吗?
  3. 我是否还缺少其他关键细节?

阅读 421

收藏
2020-03-01

共1个答案

一尘不染

区分类的初始化和对象的初始化很重要。

类初始化

首次访问时,通过分配编译时间常数字段,然后递归初始化超类(如果尚未初始化),然后处理静态初始化程序(包括用于非编译时静态字段的初始化程序),来初始化类或接口。常数)。

您已经注意到,类的初始化本身不会触发其实现的接口的初始化。因此,通常在第一次访问接口时通过读取不是编译时间常数的字段来初始化接口。这种访问可能在初始化器评估期间发生,从而导致递归初始化。

还值得一提的是,初始化不通过访问是编译时间常数领域触发,因为这些都是在评估编译时间:

对常量变量字段(第4.12.4节)的引用必须在编译时解析为常量变量的初始值设定项表示的值V。

如果此类字段是静态的,则二进制文件中的代码中不应存在对该字段的引用,包括声明该字段的类或接口。这样的字段必须始终看起来已经初始化(第12.4.2节);绝对不要遵守该字段的默认初始值(如果不同于V)。

如果这样的字段是非静态的,则除了包含该字段的类之外,二进制文件的代码中不应存在对该字段的引用。(由于接口仅具有静态字段,因此它将是一个类,而不是一个接口。)该类应具有在实例创建过程中将字段的值设置为V的代码(第12.5节)。

对象初始化

每当创建新对象时,都会初始化对象,通常是通过评估类实例创建表达式来进行初始化。其过程如下:

将构造函数的参数分配给此构造函数调用的新创建的参数变量。

如果此构造函数以同一个类中的另一个构造函数的显式构造函数调用(第8.8.7节)开头(使用此方法),则使用这五个步骤评估参数并递归处理该构造函数调用。如果该构造函数调用突然完成,则该过程由于相同的原因而突然完成;否则,请继续执行步骤5。

该构造函数并不以对同一类中的另一个构造函数的显式构造函数调用(使用此函数)开头。如果此构造函数用于Object以外的其他类,则此构造函数将以显式或隐式调用超类构造函数(使用super)开始。使用这五个相同的步骤来递归评估超类构造函数调用的参数和过程。如果该构造函数调用突然完成,则出于相同原因,此过程也会突然完成。否则,请继续执行步骤4。

执行此类的实例初始值设定项和实例变量初始值设定项,并按从左到右的顺序将实例变量初始值设定项的值分配给相应的实例变量,这些变量在文本中显示在该类的源代码中。如果执行这些初始化程序中的任何一个导致异常,则不会再处理其他初始化程序,并且该过程会因相同的异常而突然完成。否则,请继续执行步骤5。

执行此构造函数的其余部分。如果该执行突然完成,则出于相同原因,此过程也会突然完成。否则,此过程将正常完成。

正如我们在步骤3中所看到的,显式调用super构造函数的存在只是更改了调用哪个super类构造函数。

2020-03-01