Java Class 什么时候加载?

When is a Java Class loaded?

我在互联网上搜索了几个多小时,没有得出任何结论。

最近我决定将 BouncyCastle 用于 SSL,但我希望它默认关闭,因此 BouncyCastle jar 可能不在 class 路径中。

private void enableBouncyCastleForSSL() {
   if (config.isBouncyCastleEnabled()) {
        Security.insertProviderAt(new BouncyCastleProvider(), 1);
    }
} 

即使禁用配置,它也在寻找 BouncyCastle,但因 class 加载程序错误而失败。 java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider

我尝试只移动行 Security.insertProviderAt(new BouncyCastleProvider(), 1);对于一种新方法,它表现出同样的问题。

但是当我引入一个 class 并在其中移动 BouncyCastle 时,当配置被禁用时,class 加载器问题不会出现

private void setupSSLProvider() {
    if (voldemortConfig.isBouncyCastleEnabled()) {
        SetupSSLProvider.useBouncyCastle();
    }
}
public class SetupSSLProvider {
  public static void useBouncyCastle() {
    Security.insertProviderAt(new BouncyCastleProvider(), 1);
  }
}

有些文章声称Class只在第一次使用时加载。 http://www.programcreek.com/2013/01/when-and-how-a-java-class-is-loaded-and-initialized/

显然在我的例子中,Java8 加载了 class 中引用的 class。

所以我的理解是 Java 将加载 classes 一层深,然后在 class 中执行第一行代码。是这样吗?

这个问题没有简单的答案。该规范为不同的实施策略留出了空间,即使在一个实施中,它也将取决于 如何 和 class 的使用方式。此外,您的问题更多是关于“它什么时候会失败”,这取决于它可能失败的原因,例如class 可能会在某个时间点被 加载 以验证它的存在,但 初始化 在它实际上是第一个时使用的时间。在这两个时间点,操作可能会失败,导致 NoClassDefFoundError.

我们可以将引用的 class 分为三组:

  • class必须在链接时解决的问题(例如 superclass)
  • class验证时必须加载的内容
  • class可以推迟到第一次实际使用时加载

在您的情况下,BouncyCastleProvider 必须在 验证时间 加载,而不是第一次实际使用时加载,因为您传递的是 new BouncyCastleProvider() 的结果作为方法 Security.insertProviderAt(…) 的参数,验证者必须检查 class 是否实际扩展了方法的形式参数要求的 Provider 类型。

何时进行验证,也是特定于实现的,因为至少允许以下可能性:

  • 急切遍历所有引用的classes
  • 关于加载包含 class 或其首次使用
  • 关于包含方法的第一次使用
  • 就在执行违规指令之前

Oracle 的 JVM 更喜欢方法的首次使用,阅读:在方法入口处验证它,因此,将调用移动到另一个不会执行的方法中,无论是否在另一个 class 中,是对你来说足够了。

但要与其他 JVM 兼容,将其移至另一个 class 更安全,但要遵守 所有 可能的 JVM,您甚至需要加载其他class 通过反射避免直接引用,而直接引用可以被热切的实现遍历(或者首先通过 Class.forName("… .BouncyCastleProvider").newInstance() 反射实例化 BouncyCastleProvider)。