application classloader按classpath路径顺序“先到先得”查找类,只在其指定范围内(如-cp或classpath)逐个检查jar或目录中是否存在对应.class文件,命中即加载,不扫描全部路径。

Application ClassLoader(应用类加载器)并不“扫描”整个 classpath,而是按需查找——它只在你指定的 classpath 范围内,对请求加载的每个类名,从左到右依次检查路径中是否存在对应的 .class 文件或 JAR 内匹配的条目,找到第一个就停,不继续往后找。
它只认自己负责的 classpath
AppClassLoader 是真正解析 -cp、-classpath 或 CLASSPATH 环境变量的加载器。它的搜索范围就是你给定的 classpath 列表,比如:
-
lib/a.jar:lib/b.jar:classes/(Linux/macOS) -
lib\a.jar;lib\b.jar;classes\(Windows)
它不会去碰 JRE/lib/rt.jar(那是 Bootstrap 的地盘),也不会加载 JRE/lib/ext/ 下的类(那是 Extension 的职责)。它只管自己被配置的那一串路径。
查找过程是“按名定位”,不是“全盘遍历”
当你写 Class.forName("com.example.ServiceImpl"),AppClassLoader 会把包名转成路径 com/example/ServiceImpl.class,然后按 classpath 顺序逐个尝试:
- 先查
lib/a.jar里有没有这个路径;有就加载,结束 - 没有?再查
lib/b.jar;有就加载,结束 - 还没找到?最后查
classes/目录下classes/com/example/ServiceImpl.class
一旦命中,立刻返回 Class 对象;找不到才抛 ClassNotFoundException。它不会把所有 JAR 都解压、读取、比对全部类名。
classpath 顺序决定类版本优先级
同名类(如两个不同版本的 OkHttp)出现在多个 classpath 条目中时,靠前路径里的那个会被加载,后面的被忽略。这是解决冲突的关键机制,也是很多“本地跑得通、打包后报错”的根源——IDE 自动拼的 classpath 和你手动写的 -cp 不一致。
- 例如:
-cp "patch.jar:lib/http-client-4.5.jar"→patch.jar中的HttpClient会被优先使用 - 但若写成
-cp "lib/http-client-4.5.jar:patch.jar"→ 就加载不到补丁类
它不加载,只是定位 + 委托 + 读字节码
AppClassLoader 本身不直接解析字节码或验证类结构。它的典型流程是:
- 收到加载请求,先委托父加载器(Extension → Bootstrap)尝试
- 父加载器都返回 null 后,才用自己的 classpath 查找资源路径(如
getResourceAsStream("com/example/ServiceImpl.class")) - 读取到字节流后,调用
defineClass()生成 Class 对象
也就是说:classpath 提供的是“去哪里找”的线索,AppClassLoader 是那个拿着线索、按规则敲门、拿到字节码再交给 JVM 定义的执行者。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











