Class.forName和ClassLoader.loadClass的区别

本文详细对比了Class.forName()与ClassLoader.loadClass()两种加载方式的区别,包括它们如何影响类的初始化过程,并通过源码分析和实际测试加以说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

在Java中,类加载器把一个类装入Java虚拟机中,要经过三步来完成:加载、连接和初始化,其中连接又分为验证、准备和解析三个阶段。加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段可以在初始化阶段之后发生,也称为动态绑定或晚期绑定。各个步骤的主要工作如下:

  • 加载:查找和导入类或接口的二进制数据;
  • 连接:又可以分成校验、准备和解析三步,其中解析步骤是可以选择的;
    • 验证:检查导入类或接口的二进制数据的正确性;
    • 准备:给类的静态变量分配并初始化存储空间;
    • 解析:将符号引用转成直接引用;
  • 初始化:激活类的静态变量的初始化Java代码和静态Java代码块。

而类加载的方式也有以下几种方式:

  • 通过Class.forName()方法动态加载
  • 通过ClassLoader.loadClass()方法

那么通过Class.forName()加载和通过ClassLoader.loadClass加载有啥区别呢?

二、分析

1、首先看下Class.forName()源码:
@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
   
   
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

可以看到底层调用的forName0(className, true, ClassLoader.getClassLoader(caller), caller),第二个参数initialize表示是否执行类中的静态代码块、对类中的静态变量赋值等初始化操作,默认给的true。

2、在Class类中还有另一个方法,可以自己传入initialize的值,源码如下:
/**
* @param name       fully qualified name of the desired class
* @param initialize if {@code true} the class will be initialized.
*                   See Section 12.4 of <em>The Java Language Specification</em>.
* @param loader     class loader from which the class must be loaded
* @return           class object representing the desired class
*
* @exception LinkageError if the linkage fails
* @exception ExceptionInInitializerError if the initialization provoked
*            by this method fails
* @exception ClassNotFoundException if the class cannot be located by
*            the specified class loader
*
* @see       java.lang.Class#forName(String)
* @see       java.lang.ClassLoader
* @since     1.2
*/
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
                               ClassLoader loader)
    throws ClassNotFoundException
{
   
   
    Class<?> caller = null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null)