Class.forName() 不仅加载类,还默认执行静态初始化;其重载版本可通过 initialize 参数控制是否初始化,适用于JDBC驱动注册等场景,但需警惕阻塞、异常掩盖及安全风险。

Class.forName() 不只是“加载一个类”,它实际触发了 JVM 类加载全流程,并默认执行静态初始化——这个细节在数据库驱动注册、插件热加载等场景中,直接决定功能是否生效。
Class.forName() 的真实行为:加载 + 初始化
调用 Class.forName("com.mysql.cj.jdbc.Driver") 时,JVM 不仅把字节码读入内存,还会立即执行该类的 static 块(比如 DriverManager.registerDriver(...))。这是它和 ClassLoader.loadClass() 的关键区别:后者只走到“加载+连接”阶段,跳过初始化。
这种默认初始化行为是双刃剑:
- ✅ 优点:适配 JDBC 4.0 之前规范,驱动靠 static 块自注册,必须初始化才有效
- ❌ 风险:若目标类 static 块含耗时操作、资源独占逻辑或异常抛出,会阻塞当前线程,甚至导致 ClassNotFoundException 被掩盖为 ExceptionInInitializerError
两个重载版本:控制初始化开关
除了最常用的单参数形式,Class.forName(String name),还有带三个参数的重载:
Class.forName(String name, boolean initialize, ClassLoader loader)
其中第二个参数 initialize 决定是否执行静态初始化:
- 传 true(默认行为):加载并初始化,static 块运行,静态字段赋值完成
- 传 false:仅加载类,不执行 static 块——适合需延迟初始化、或仅需获取类结构(如注解扫描、类型判断)的场景
第三个参数可显式指定类加载器,避免因上下文类加载器(ContextClassLoader)与预期不符导致类找不到。
典型生产场景与避坑要点
在真实项目中,Class.forName() 常见于以下环节,但容易忽略隐含约束:
- JDBC 驱动加载:旧版代码常写 Class.forName("oracle.jdbc.driver.OracleDriver")。JDBC 4.0+ 已支持 SPI 自动发现,无需手动调用;若仍保留,需确保驱动 JAR 在 classpath,且类名拼写完全匹配(含大小写、包路径)
- 配置化策略类加载:例如根据 spring.profiles.active 加载不同实现类。务必捕获 ClassNotFoundException,并提供明确 fallback 或日志,不能静默吞掉异常
- 插件/模块热加载:若插件类由自定义 ClassLoader 加载,必须传入该 loader 实例,否则会走系统类加载器,导致“类已存在但无法转型”或 LinkageError
安全提示:禁止将用户输入(如 HTTP 参数、配置项)未经白名单校验就拼接进 Class.forName()。恶意构造的类名可能触发任意类加载,引发反序列化、RCE 等风险。
替代方案对比:什么情况下不该用 forName
并非所有“按名字找类”都该用 Class.forName():
- 编译期已知类 → 直接用 User.class:无异常、不触发初始化、性能最优
- 仅需类型检查、不执行 static 块 → 用 ClassLoader.loadClass():更轻量,可控性更强
- 跨模块/OSGi/模块化环境 → 优先通过服务接口(ServiceLoader)或依赖注入容器获取实例,而非硬编码类名
一句话总结:Class.forName() 是动态性的入口,但它的力量来自初始化,也受制于初始化——用之前,先想清楚:你到底要不要那块 static 代码跑起来。










