0

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

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 关系,它们会在堆栈序列中初始化?

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

4

2 回答 2

4

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

C 的静态初始化程序必须在创建 C 的实例之前完成,因为:

类或接口类型 T 将在以下任何一项第一次出现之前立即初始化:

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

B 的静态初始化程序必须在 C 之前运行,A 在 B 之前运行,因为:

在一个类被初始化之前,它的直接超类必须被初始化,

于 2020-07-04T02:45:10.033 回答
1

我不知道您所说的“堆栈序列”是什么意思;让我看看我是否可以解释我的预期,这就是我认为它必须去的方式。

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

它去获取 C 类,并据此确定它必须有 B 才能执行诸如执行 C 静态初始化程序(static { ... }事物)之类的事情。它去了并获得了B级。

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

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

将 Object 类放入内存并运行其静态初始化程序后,就可以按顺序运行 A、B 和 C 的静态初始化程序了。就我们在​​这里讨论的内容而言,这就完成了设置 C 类的部分。你可以称之为“堆栈顺序”;这就像 C 调用了 B,它调用了 A,它调用了 Object,实际上这可能是它的实现方式(或者它可能是如何实现的)。

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

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

我不是 Java 运行时专家,但这些序列必须是这样的。如果您更改 A 的静态初始化程序,则上述序列指示何时必须运行该代码;我没有看到任何其他方式可以工作。

于 2020-07-04T02:46:35.737 回答