Java 的初始化和实例化顺序

标签 java

我正在尝试将 JVM 中的初始化和实例化过程拼凑起来,但 JLS 在一些细节上有点迟钝,所以如果有人愿意澄清一些细节,我们将不胜感激。到目前为止,这是我能够弄清楚的。

初始化

  1. 递归地初始化类的静态最终变量及其作为编译时常量的接口(interface)。

  2. 按文本顺序退出递归处理静态 block 和静态字段。

实例化

  1. 递归地初始化作为编译时常量的类的最终实例变量。

  2. 退出以文本顺序处理非静态 block 和实例字段的递归处理,在返回时将它们添加到构造函数。


好的,现在开始提问。

  1. 接口(interface)是否按声明顺序处理?

  2. 接口(interface)是否在单独的递归堆栈中处理?

    a) 如果是,接口(interface)是在父类(super class)之前还是之后处理的?

    b) 如果是,我是否正确推断出其中一个或其他(接口(interface)或父类(super class))在其他编译时常量之前初始化其非编译时常量字段。

  3. 调用非默认 super() 构造函数在这个过程中扮演什么角色?

  4. 我的结论有误吗?

  5. 我是否遗漏了任何其他关键细节?

最佳答案

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

类初始化

根据 first access 初始化一个类或接口(interface),通过分配编译时常量字段,然后递归初始化父类(super class)(如果尚未初始化),然后处理静态初始化程序(包括非编译时常量的静态字段的初始化程序)。

正如您所注意到的,类的初始化本身并不会触发它实现的接口(interface)的初始化。 接口(interface)在第一次被访问时被初始化,通常是通过读取一个不是编译时间常量的字段。这种访问可能发生在初始化器的评估期间,导致递归初始化。

还值得注意的是,访问是编译时常量的字段不会触发初始化,因为这些字段在 compile time 处进行评估。 :

A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable's initializer.

If such a field is static, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field. Such a field must always appear to have been initialized (§12.4.2); the default initial value for the field (if different than V) must never be observed.

If such a field is non-static, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has only static fields.) The class should have code to set the field's value to V during instance creation (§12.5).

对象初始化

一个对象被初始化 whenever a new object is created ,通常通过评估类实例创建表达式。过程如下:

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  2. If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.

  3. This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

  4. Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.

  5. Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

正如我们在第 3 步中看到的,对超构造函数的显式调用只是改变了调用哪个父类(super class)构造函数。

关于Java 的初始化和实例化顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23093470/

相关文章:

c# - 发送 IP 数据包

java - 使用 Mockito 测试 Mono 和 Flux

java - Android 中 Dropbox 中的内部服务器错误

java - 构造函数中的“Collections.unmodifyingCollection”

java - 是否可以使用 java 将 EBCDIC Comp-3 文件转换为 ASCII 文件值?

java - 使用 JDBC 的 NullPointerError

java - 如何在一行中准确输入五个字符的字符串,然后输出每个字符之间有空格的字符串? java

java - ArrayDeque 大小和内容

java - 如何正确触发 `fireTableDataChanged`

java - JDialog 不显示组件