为什么.class不调用类中的静态块?

2022-08-31 14:12:45

这是我的代码:

public class StupidClass {
    static {
        System.out.println("Stupid class loaded!");
    }
}

还有我拥有的测试,我单独运行。

import org.junit.Test;

public class StupidTest {
    @Test
    public void foo() throws ClassNotFoundException {
        final Class<?> stupidClass = Class.forName("StupidClass");
        System.out.println(stupidClass.getSimpleName());
    }

    @Test
    public void bar() throws ClassNotFoundException {
        final Class<StupidClass> stupidClassClass = StupidClass.class;
        System.out.println(stupidClassClass.getSimpleName());
    }
}

当我运行测试foo时,我会看到:

Stupid class loaded!
StupidClass

但是当我运行测试时,我看到的只是:

StupidClass

引用此页面..

类对象由 Java 虚拟机在装入类时自动构造,并通过调用类装入器中的 defineClass 方法自动构造。

所以我的理解是,在测试栏中,愚蠢的类被加载,否则我会看到一个空的我猜?因此,创建类对象是因为类本身已加载。

现在引用此页面

静态初始化块在 JVM(类装入器 - 具体而言)加载 StaticClass(首次在代码中引用它时发生)时运行。

所以我也期望在测试栏中看到“愚蠢的类加载!”文本,但我不是。

还引用了《Thinking in Java》一文

每个类 Candy、Gum 和 Cookie 都有一个静态子句,该子句在首次加载类时执行。

这似乎不是很准确。

我错过了什么?


答案 1

静态初始化块在 JVM(类装入器 - 具体而言)加载 StaticClass(首次在代码中引用它时发生)时运行。

上面的引文是完全错误的,但这只是一个非常普遍的误解的一个例子。

  1. 类在装入时不会初始化,而是在首次引用静态类成员时初始化。这完全由规范控制。

  2. 首次引用类时不会发生类装入,而是在与实现相关的点上进行装入。

  3. 必须装入类的最后时刻是引用类时,这与引用类成员不同

Class.forName默认情况下初始化类,但您可以选择调用一个重载,该重载采用 a 并提供 。您将在不初始化的情况下加载类。boolean initializefalse


答案 2

类加载和初始化是两回事。可以加载类,但在真正需要之前不对其进行初始化。静态初始值设定项仅在类被初始化<>未加载、“初始化”时运行

在第一种情况下,您在使用 时加载和初始化类,这就是运行静态初始值设定项的原因,因此您会看到输出 。在第二种情况下,您只是分配类的引用,类被加载(使用java -verbose:class查看加载了哪些类),但您并没有真正初始化它(或者更准确地说,不做任何强制初始值设定项运行的事情)。因此,您不会看到输出为 .尝试做一些类似调用类的事情,它应该强制初始化类,你应该看到class.forName()"Stupid class loaded!"Stupid class loaded!newInstance()Stupid class loaded!

我的代码 :

public class CheckPalindrome {

    public static void main(String[] args) {
        Class<Test> t = Test.class;
    }

}
// class being loaded
class Test {
    static {
        System.out.println("aaa");
    }
}

装入的类

...
[Loaded Test from file:/Workspaces/SampleTest/Java8/bin/]
...

^ - 这表示类已加载但未初始化。