1. 概述

在Java中,动态加载类是指在运行时而不是编译时将类加载到Java虚拟机(JVM)。这种方法在我们不知道编译时类名,或者类加载基于用户输入或系统属性的情况下特别有用。

Java中有多种方式动态加载类,包括Class.forName()方法、ClassLoader API以及依赖注入框架。本文将关注Class.forName()Class.forName().newInstance()方法,它们在Java应用中常见且对开发者理解至关重要。

2. Class.forName()方法

Class.forName()方法接受一个完整的类名作为参数,并返回与已加载类关联的Class对象。在执行过程中,它尝试查找、加载并链接类或接口。当我们想要获取关于类的信息,如字段和方法时,通常会使用它。然而,需要注意的是,类并未初始化,调用其方法是不可能的

让我们编写一个快速测试,使用Class.forName()方法:

@Test
public void whenUseClassForNameCreatedInstanceOfClassClass() throws... {
   Class instance = Class.forName("com.baeldung.loadclass.MyClassForLoad");
   assertInstanceOf(Class.class, instance, "instance should be of Class.class");
}

还要记住,动态加载方法并不检查请求类对其调用者的可访问性,可能导致与加载、链接或初始化相关的潜在异常。妥善处理这些异常至关重要,以避免意外行为和可能的崩溃:

  • LinkageError - 如果链接失败
  • ExceptionInInitializerError - 如果初始化失败
  • ClassNotFoundException - 如果找不到类

3. Class.forName().newInstance()方法

当需要创建一个类的实例时,Class.forName().newInstance()方法更为合适。此方法同样接受一个完整的类名作为参数,并返回代表此Class对象的类的新实例。这就像为MyClassForLoad调用带有空参数列表的new操作符。

让我们看一个使用Class.forName().newInstance()方法的单元测试示例:

@Test
public void whenUseClassForNameWithNewInstanceCreatedInstanceOfTargetClass throws... () {
   Object instance = Class.forName("com.baeldung.loadclass.MyClassForLoad").newInstance();
   assertInstanceOf(MyClassForLoad.class, instance, "instance should be of MyClassForLoad.class");
}

请记住,Class.forName().newInstance()可能会抛出几个异常:

  • IllegalAccessException - 如果类或其无参构造函数不可访问
  • InstantiationException - 如果这个类表示抽象类、接口、数组类、基本类型或void
  • ExceptionInInitializerError - 如果由此方法引发的初始化失败
  • SecurityException - 当调用代码没有足够的权限访问指定的类

然而,重要的是要注意,自Java 9起,newInstance()方法已被弃用,因为它会传播由无参构造函数抛出的任何异常,包括检查异常。这实际上绕过了编译器进行的编译时异常检查。因此,可能导致意外错误或bug,使得代码库更难维护和扩展。

**推荐使用getDeclaredConstructor().newInstance()**方法来避免这个问题:

Object instance = Class.forName("com.baeldung.loadclass.MyClassForLoad").getDeclaredConstructor().newInstance();

4. 总结

开发者必须深入了解Java中动态类加载的工作原理。它提供了许多优势,有助于提高Java应用程序的性能、可维护性和可扩展性。

本文介绍了Class.forName()Class.forName().newInstance()是Java中的关键方法,它们允许我们在运行时加载类。这两种方法的区别在于它们的作用和返回值。

Class.forName()动态加载类并返回一个Class对象,而Class.forName().newInstance()加载类并创建加载类的实例。

理解和区别这些方法对于实际场景中的应用至关重要,例如:

  • 插件开发
  • 如Spring或Guice这样的依赖注入框架
  • 根据使用的数据库加载JDBC驱动
  • 根据输入参数加载类

最后,通过了解Class.forName()Class.forName().newInstance()之间的差异,开发者可以为其Java项目做出明智的动态加载类决策,从而实现更高效和有效的代码。完整的源代码可在GitHub上找到。