Java中forName和loadClass的区别

本文对比了Class.forName和ClassLoader.loadClass两种方法加载JDBC驱动的区别,深入解析了类的加载过程,并解释了为何某些方法更适合加载数据库驱动。

问题描述

在JDBC编程中,经常会看到Class.forName(“com.mysql.jdbc.Driver”),加载数据库驱动类,当时在java中还有一种加载类的方法即getClass.getClassLoader().loadClass(“com.mysql.jdbc.Driver”),但是当我使用此方法加载数据库驱动类时,发现无法成功加载。因此觉得这两个方法应该是有些区别的。这里就比较一下二者的区别。在讲区别之前,我觉得应该先整理一下类的加载过程。

类的加载过程

Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三个步骤。除了解析外,其他步骤是严格按照顺序完成的,各个步骤的主要工作如下:
装载: 查找和导入类或接口的二进制数据;
链接: 执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
校验: 检查导入类或接口的二进制数据的正确性;
准备: 给类的静态变量分配并初始化存储空间;(注意是静态变量,类的实例变量是要等到你新建此类的对象时,才进行分配内存和初始化)
解析: 将符号引用转成直接引用;
初始化: 先运行静态代码块,之后进行静态变量的初始化;

两者区别

Class.forName(className),其实调用的方法是Class.forName(className,true,classloader);注意第二个boolean参数,它表示在装载类之后必须初始化。而看下我们上一节讲的JVM加载类的知识,我们可以看到执行过此方法后,目标对象的static块代码已经被执行,static参数也已经被初始化了
而ClassLoader.loadClass(className)方法,其实调用的方法是ClassLoader.loadClass(className,false);第二个boolean参数表示目标类被装载后不进行链接,这也就意味着不会去执行该类静态块中的内容了。因此二者的区别就显而易见了。
回到我们最初的问题,为什么用ClassLoader.loadClass(className)方法加载数据库驱动类就不行呢?打开com.mysql.jdbc.Driver的源代码,其中的static块代码如下:

static {
    try {
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
} 

Driver在static块中会注册自己到java.sql.DriverManager。而static块就是在Class的初始化中被执行。所以这个地方就只能用Class.forName(className)。

### 三级标题:加载机制与初始化行为 `Class.forName("className")` 是一个静态方法,用于在运行时加载类,并且它会触发类的初始化。这意味着除了加载类的字节码并将其转换为JVM内部表示之外,还会执行类中的静态代码块静态变量初始化器。例如: ```java try { Class<?> myClass = Class.forName("com.example.MyClass"); } catch (ClassNotFoundException e) { e.printStackTrace(); } ``` 此方法通常用于需要动态加载类并在加载时立即初始化的场景,如数据库驱动程序加载[^3]。 相比之下,`ClassLoader.loadClass("className")` 是一个实例方法,由类加载器对象调用。它仅负责加载类而不执行任何初始化操作。只有当类首次被主动使用时(例如创建实例或访问静态字段),才会进行初始化。示例如下: ```java ClassLoader classLoader = getClass().getClassLoader(); try { Class<?> myClass = classLoader.loadClass("com.example.MyClass"); } catch (ClassNotFoundException e) { e.printStackTrace(); } ``` 这种方式适用于延迟初始化的需求,可以提高应用程序启动性能。 ### 三级标题:异常处理差异 使用 `Class.forName()` 加载类时,如果指定的类无法找到或加载失败,则会抛出 `ClassNotFoundException` 异常。这使得错误处理更加直接明确。 而 `ClassLoader.loadClass()` 方法在找不到类时不会抛出异常,而是返回 `null`。开发者必须检查返回值以确定是否成功加载了类。如果后续尝试使用未正确加载的类,则可能导致 `NoClassDefFoundError` 或其他运行时错误[^3]。 ### 三级标题:调用链与参数控制 `Class.forName(String className)` 实际上调用了 `Class.forName(className, true, ClassLoader.getCallerClassLoader())` 方法。其中第二个参数控制是否初始化类,第三个参数指定了使用的类加载器[^4]。 通过提供更详细的参数选项,`Class.forName()` 允许开发者对类加载过程有更细粒度的控制。例如,可以通过设置初始化标志为 `false` 来避免立即初始化类,从而模仿 `ClassLoader.loadClass()` 的行为[^4]。 ### 三级标题:应用场景对比 - **Class.forName()** 更适合那些需要立即初始化类的情况,比如注册 JDBC 驱动等。 - **ClassLoader.loadClass()** 则更适合于需要延迟初始化或者只需要类的结构信息而不关心其初始化逻辑的情形,比如插件系统中预加载类但不立即使用它们。 综上所述,选择哪种方式取决于具体需求:如果需要确保类被正确初始化,则应使用 `Class.forName()`;如果希望延迟初始化或仅需类的定义,则可选用 `ClassLoader.loadClass()` [^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值