如果 Java 中的类具有 IS-A 关系,它们是否按堆栈序列初始化?



让我通过代码示例来解释我的问题,我有以下代码:

class A {
public A() {
System.out.println("In class A constructor");
}
static {
System.out.println("In class A static initializer");
}
}
class B extends A {
static {
System.out.println("In class B static initializer");
}
public B() {
System.out.println("In class B constructor");
}
}

public class C extends B {
public C() {
System.out.println("In class C constructor");
}
static {
System.out.println("In class C static initializer");
}
public static void main(String[] args) {
new C();
}
}

如果我们运行此代码,我们将打印出控制台:

在类 A 中 静态初始值设定项

在 B 类中 静态初始值设定项

在 C 类静态初始值设定项中

在 A 类构造函数中

在 B 类构造函数中

在 C 类构造函数中

如您所见,类中的所有静态字段首先从 A 类向下调用到 B 类,然后调用 C 类。然后我读到了静态初始值设定项

在类

中声明的静态初始值设定项在类 初始 化

我知道如果我们只研究构造函数。当我们调用new C()时,我们将有一个调用堆栈:

"5"Object()

"4"A()呼叫super()

"3"B()呼叫super()

"2"C()呼叫super()

"1"main()呼叫new C()

那么,为什么类 A、B、C 中的所有静态初始值设定项都首先完成,然后是构造函数呢?这是否意味着Java中的类在具有IS-A关系的情况下按堆栈序列初始化?

我想我的问题可以通过这里详细初始化过程来回答,但我对所有细节都迷失了方向。希望有人能向我解释一下。

构造函数在创建实例后运行。

C的静态初始值设定项必须在创建 C 实例之前完成,因为:

类或接口类型 T 将在以下任一情况首次出现之前立即初始化:

T
  • 是一个类,创建 T 的实例。

B 的静态初始值设定项必须在 C 之前运行,A 必须在 B 之前运行,因为:

在初始化类之前,必须初始化其直接超类,

我不知道你所说的"堆栈序列"到底是什么意思;让我看看我是否可以解释我的期望如何进行,这就是我认为它必须如何进行。

Java 运行时开始执行 C.main;为此,它必须初始化 C 类对象(不是 C 的实例,而是类 C(。

它去获取 C 类,并从中确定它必须具有 B 才能执行 C 静态初始值设定项(static { ... }的事情(。它去并获得B类。

它确定,在执行 B 静态初始值设定项之前,它需要 A,然后去获取它。请记住,关于 A 的一些事情 B 不知道,除了它们包含在 A 中,所以在 A 类存在并初始化之前,它根本无法对 B 做任何事情。

获得 A 后,它可能会确定它需要 Object 类对象(谈论您在语言运行时中实现的语言所需的语言扭曲必须驱动这些人 NUTS(并运行其静态初始值设定项。

一旦它将 Object 类放入内存并运行其静态初始值设定项,它就可以按该顺序运行 A、B 和 C 的静态初始值设定项。就我们在这里讨论的内容而言,这完成了设置 C 类的部分。您可以称之为"堆栈顺序";这就像 C 调用 B,B 调用 A,调用 Object,事实上,这可能是它的实现方式(或实现方式(。

现在,C 构造函数已准备好创建 C 的实例,并且同样类型的逻辑适用。在构造 B 之前,它不能执行 C 构造函数的一点,在构造 A 之前不能执行一点 B 构造函数,在构造 Object 之前不能执行一点 A 构造函数。这就是为什么super()如果要在那里,它必须是构造函数中的第一件事;如果不存在,运行时将在超类上执行 no-arg 构造函数。

可以说每个级别的静态初始值设定项都已启动,但它们的第一步是运行其超类的静态初始值设定项,构造函数也是如此。无论你说一个代码启动然后运行超级代码,还是超级代码在目标代码之前运行,在我看来都没有太大区别。

我不是 Java 运行时专家,但这些序列必须如此。如果更改 A 的静态初始值设定项,则上述序列指示何时必须运行该代码;我看不出任何其他方法可以工作。

最新更新