使用 ClassLoader 和 Class.forName 装入类之间的区别

2022-09-01 19:40:54

下面是 2 个代码片段

第一个使用 ClassLoader 类来加载指定的类

ClassLoader cls = ClassLoader.getSystemClassLoader(); Class someClass = cls.loadClass("TargetClass");

第二个使用 Class.forName() 来加载指定的类

Class cls = Class.forName("TargetClass");

上述方法有什么区别。哪一个用于哪个目的?


答案 1

其他答案非常完整,因为它们探讨了 的其他重载,并讨论了使用不同 ClassLoaders 的可能性。Class.forName(...)

但是,他们没有回答您的直接问题:“上述方法之间有什么区别?”,它涉及一个特定的重载。他们错过了一个非常重要的区别。类初始化Class.forName(...)

请考虑以下类:

public class A {
  static { System.out.println("time = " + System.currentTimeMillis()); }
}

现在考虑以下两种方法:

public class Main1 {
  public static void main(String... args) throws Throwable {
    final Class<?> c = Class.forName("A");
  }
}

public class Main2 {
  public static void main(String... args) throws Throwable {
    ClassLoader.getSystemClassLoader().loadClass("A");
  }
}

第一个类 ,在运行时将生成一个输出,例如Main1

time = 1313614183558

但是,另一个将根本不产生任何输出。这意味着类虽然已加载,但尚未初始化(即,它尚未被调用)。实际上,您甚至可以在初始化之前通过反射来查询类的成员!A<clinit>

你为什么会关心?

有些类在初始化时执行某种重要的初始化或注册。

例如,JDBC 指定由不同提供程序实现的接口。要使用MySQL,您通常要执行.也就是说,加载并初始化该类。我从未见过该代码,但显然该类的静态构造函数必须在JDBC的某个地方注册该类(或其他东西)。Class.forName("com.mysql.jdbc.Driver");

如果这样做,您将无法使用 JDBC,因为 altough loaded 类尚未初始化(然后 JDBC 将不知道要使用哪个实现,就像您没有加载该类一样)。ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver");

因此,这就是您询问的两种方法之间的差异。


答案 2

快速回答(无代码示例)

使用显式方法,您可以灵活地从不是默认类加载器的类加载器加载类。在你的例子中,你使用的是默认的系统类加载器,所以它给出了与调用类似的总体结果(带有最终对象差异的实例化),但你可以引用另一个类加载器。ClassLoader cls = <a ClassLoader>;Class.forName(String name)

也就是说,只要您知道ClassLoader是什么,您也可以使用。Class.forName(String name, boolean initialize, ClassLoader loader)

例如,基于 EAR 的应用程序具有自己的 ClassLoader,其中包含一个 XML 解析库版本。您的代码通常使用这些类,但在一个实例中,您需要从早期版本的库(应用程序服务器恰好在其整个 ClassLoader 中保存该库)中获取反序列化类。因此,您可以改为引用该应用程序服务器类加载器。

不幸的是,在我们得到项目Jigsaw(JDK 8)之前,这比我们想要的更频繁地使用:-)