java类加载核心在于三点:初始化才是触发加载的真正时机;类由“类名+加载器实例”共同唯一标识;打破双亲委派是按需反转委派顺序,如tomcat优先自加载以实现应用隔离。

一、初始化才是真正的“触发点”
很多人以为类一启动就全被加载了,其实JVM是懒的——只在首次主动使用时才初始化。这个“主动使用”有明确清单:
- 用
new创建实例 - 调用类的静态方法
- 读写静态变量(注意:只读
final static常量不算) - 反射调用
Class.forName("xxx")(带初始化参数为true时) - 初始化子类时,父类会先被初始化(哪怕子类只访问父类的静态字段)
反例:通过子类引用父类的静态变量、定义类数组、单纯获取Class对象(如MyClass.class),都不会触发初始化。
二、类加载器不是“一个”,而是“一套+一个实例”
JVM自带三层加载器,但真正起作用的是类加载器实例 + 类全限定名的组合。同一个类名,被不同加载器实例加载,JVM就当它是两个完全不同的类。
-
Bootstrap:C++实现,加载
rt.jar等核心类,没有Java对象,getClassLoader()返回null -
Extension:加载
lib/ext目录,父加载器是Bootstrap -
Application:加载
-cp路径,父加载器是Extension -
自定义加载器:必须继承
ClassLoader,通常重写findClass,而不是loadClass
重点:判断两个类是否相同,看的是“类名 + 加载它的那个加载器对象”,缺一不可。
三、打破双亲委派不是“破坏”,而是“按需反转”
双亲委派是默认安全策略,但某些场景必须绕过它,比如:
-
Web应用隔离:Tomcat为每个应用配独立
WebAppClassLoader,优先加载WEB-INF/classes和lib,不先问父加载器 - 热加载更新:旧类卸载的前提是旧加载器被GC回收,所以得新建加载器加载新字节码,再替换引用
- 插件/模块化系统:不同插件可能含同名类但不同版本,需各自加载、互不干扰
实现方式很直接:重写loadClass方法,把“先委托父类”逻辑删掉或后置,改成“先自己找,找不到再委托”。注意别漏掉findLoadedClass检查和resolveClass调用。
四、卸载类?条件比加载还苛刻
很多人想“重新加载类”,却卡在卸载不上。类能被卸载,必须同时满足:
- 该类的所有实例都已不可达(无强引用)
- 该类的
ClassLoader实例本身也被GC回收 - 该类的
java.lang.Class对象也无引用
所以热部署实践中,往往不是“卸载旧类”,而是丢弃整个加载器实例,让GC自然清理,再用新加载器加载新版字节码。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











