Class.forName触发类初始化而ClassLoader.loadClass不触发:前者执行静态块完成加载→链接→初始化,后者仅到链接阶段;前者抛ClassNotFoundException,后者返回null;前者用TCCL,后者依赖指定ClassLoader实例。

隐式加载和显式加载,本质是类被引入 JVM 的方式不同:前者由 JVM 自动触发,后者由开发者主动发起。Class.forName 和 ClassLoader.loadClass 都属于显式加载,但它们在类加载流程中所处的阶段、是否触发初始化、异常行为和类加载器来源都有关键区别。
隐式加载:不用写代码,JVM 自己决定什么时候加载
当你用 new 创建对象、访问静态字段、调用静态方法、或继承/实现某个类时,JVM 会在真正需要时自动完成类的加载、链接和初始化。比如:
-
User user = new User();→ JVM 自动加载并初始化User类 -
System.out.println(User.VERSION);→ 触发User类初始化(执行 static 块)
这种加载对开发者透明,不涉及任何 API 调用,也不受你手头 ClassLoader 实例影响。
Class.forName:显式加载 + 立即初始化
它不只是把类“加载进来”,而是走完完整的三步:加载 → 链接 → 初始化。也就是说,静态代码块和静态字段赋值会立刻执行。
立即学习“Java免费学习笔记(深入)”;
- JDBC 场景中
Class.forName("com.mysql.cj.jdbc.Driver")就是为了让驱动类里的static { DriverManager.registerDriver(...); }运行起来 - 找不到类时抛
ClassNotFoundException(必须 try-catch) - 默认使用当前线程上下文类加载器(Thread Context ClassLoader),适合跨模块场景
ClassLoader.loadClass:显式加载,但只到链接阶段
它只完成加载和链接(验证、准备、解析),跳过初始化。静态代码块不会执行,直到你第一次真正用到这个类——比如 new 实例、访问静态成员、调用静态方法。
- 适合插件系统、热更新、注解扫描等“先拿到 Class 再决定要不要用”的场景
- 找不到类时返回
null,不是抛异常,容易引发空指针,必须判空 - 必须基于一个具体的 ClassLoader 实例(如
MyClass.class.getClassLoader()),类加载器选择更明确,但也更受限
怎么选?看你要不要“立刻执行副作用”
如果目标类有静态注册逻辑(如 JDBC 驱动、SPI 服务提供者配置)、或者你马上就要反射调用它的静态方法,就用 Class.forName;如果你只是想检查类是否存在、获取其注解信息、或延迟触发初始化,loadClass 更轻量、更可控。
两者都不是“替代 new”的工具,而是控制类何时进入 JVM 生命周期不同阶段的手段。理解它们,就是理解 Java 类加载的时机与责任边界。


















