如何在Sun的JVM中禁用延迟类加载/初始化?

jon*_*jon 12 java class classloader static-initializer

默认情况下,Sun的JVM都懒惰地加载类并且懒惰地初始化(即调用它们的<clinit>方法)它们.考虑以下类,ClinitBombExceptionstatic{}块中抛出一个.

public class ClinitBomb {
    static {
        explode();
    }   
    private static void explode() {
        throw new RuntimeException("boom!");
    }       
}
Run Code Online (Sandbox Code Playgroud)

现在,考虑如何触发炸弹:

public class Main {
    public static void main(String[] args) {
        System.out.println("A");
        try {
            Class.forName("ClinitBomb");
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
        System.out.println("B");
        ClinitBomb o2 = new ClinitBomb();
        System.out.println("C");
    }
}
Run Code Online (Sandbox Code Playgroud)

我们保证爆炸发生在B点之前,因为forName文件说明了这一点; 问题是它是否发生在A点之前(Main加载时).在Sun的JVM中,即使main()包含静态引用ClinitBomb,它也会在A之后发生.

我想要一种方法告诉JVM ClinitBomb一旦初始化就加载并初始化Main(因此炸弹 A点之前爆炸).一般来说,我想要一种方式来说,"无论何时加载/初始化类X,对任何一个也是如此它引用的Y类."

有没有办法做到这一点?

Tom*_*son 9

没有办法做到这一点.JLS在第12.4.1节中说,当初始化发生时(强调我的):

类的初始化包括执行其静态初始化程序和类中声明的静态字段的初始化程序.[...]

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

  • T是一个类,并且创建了T的实例.
  • T是一个类,并且调用由T声明的静态方法.
  • 分配由T声明的静态字段.
  • 使用由T声明的静态字段,该字段不是常量变量(第4.12.4节).
  • T是顶级类,并且执行在词典内嵌套在T中的断言语句(第14.10节).

在类Class和包java.lang.reflect中调用某些反射方法也会导致类或接口初始化.在任何其他情况下,不会初始化类或接口.

一旦加载了类就初始化的Java实现会违反JLS.

虽然你可以做的是使用JVM 工具API编写一个ClassFileTransformer,它为每个显式初始化其引用类的类添加一个静态块(可能通过Class.forName).只要一个类被初始化,就可以初始化从它可以访问的所有类.这可能会给你你想要的结果.不过,这是相当多的工作!