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

默认情况下,Sun的JVM都懒惰地加载类并且懒惰地初始化(即调用它们的方法)它们。 考虑以下类ClinitBomb ,它在static{}块期间抛出Exception

 public class ClinitBomb { static { explode(); } private static void explode() { throw new RuntimeException("boom!"); } } 

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

 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"); } } 

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

我想要一种方法告诉JVM一旦初始化Main就加载并初始化ClinitBomb (因此炸弹 A点之前爆炸)。一般来说,我想要一种方式来说,“无论何时加载/初始化类X,也都这样做对于Y所引用的任何类别。“

有没有办法做到这一点?

没有办法做到这一点。 JLS说,在§12.4.1初始化发生时 (强调我的):

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

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

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

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

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

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

 Class.forName("...", true /*initialize*/, getClassLoader()); 

你在那里一半。