Java动态加载类核心靠ClassLoader与反射配合,支持Class.forName()、URLClassLoader、运行时编译及自定义ClassLoader四种方式,分别适用于延迟初始化、外部JAR加载、动态源码编译和精细字节码控制场景。

Java 在运行时动态加载类,核心靠的是 ClassLoader 机制和反射配合,不是编译期绑定,而是根据字符串类名、字节码来源或配置在程序启动后按需加载。关键不在于“能不能”,而在于“怎么控制加载时机、来源和隔离性”。
用 Class.forName() 快速加载已存在类
这是最轻量的动态加载方式,适用于类已在 classpath 中、只需延迟初始化的场景:
-
Class.forName("com.example.ServiceImpl")会触发类加载 + 静态初始化(执行 static 块) - 若只想加载不初始化,改用
Class.forName("...", false, currentClassLoader),第二个参数设为false - 必须捕获
ClassNotFoundException,类名拼错或路径不对都会抛这个异常 - 适合驱动注册、配置化实现切换(如日志实现、序列化器)
用 URLClassLoader 加载外部 .class 或 JAR
当类不在默认 classpath,比如从磁盘目录、临时文件夹或远程下载的 JAR 中加载时,用 URLClassLoader:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 把目标路径转成
File,再转为URL,传入构造函数:new URLClassLoader(new URL[]{jarUrl}, parentLoader) - 默认遵循双亲委派,父加载器先尝试;如需绕过(例如热替换同名类),需继承并重写
loadClass() - 加载后调用
cl.loadClass("com.example.Plugin")获取 Class 对象,再用反射实例化 - 注意:JAR 文件必须结构正确(含 MANIFEST.MF 和合法包路径),否则
NoClassDefFoundError
运行时编译 + 加载(内存或文件方式)
如果连源码都是动态生成的(如规则引擎、脚本沙箱),需走“编译→字节码→加载”三步:
立即学习“Java免费学习笔记(深入)”;
- 调用
ToolProvider.getSystemJavaCompiler()—— 要求运行环境是 JDK,JRE 不支持 - 编译目标目录必须提前创建,并通过
-d参数指定;依赖类路径用-cp显式传入 - 推荐先将源码写入临时文件再编译,比纯内存编译(需自定义
JavaFileManager)更稳定 - 编译成功后,用
URLClassLoader指向输出目录,即可加载新类
自定义 ClassLoader 实现精细控制
标准加载器不够用时(比如从数据库读字节码、网络加载、类版本隔离),可继承 ClassLoader:
- 重写
findClass(String name),不走双亲委派,直接获取字节码数组 - 用
defineClass(name, bytes, offset, len)把字节码转成 Class 对象 - 常配合缓存、校验、加密等逻辑,例如插件系统中每个插件用独立加载器,避免类冲突
- 注意:类卸载依赖加载器对象被回收,所以要避免静态引用导致内存泄漏

















