初学者区分java类加载器应抓住职责范围和加载路径:bootstrap加载java_home/lib核心类(返回null),extclassloader加载lib/ext扩展类,appclassloader加载classpath用户类,且遵循双亲委派机制。

初学者区分 Java 中不同的类加载器,关键不是死记名称,而是抓住职责范围和加载路径这两个最直观的判断依据。只要知道“谁负责加载哪部分代码”,就能快速分辨。
看加载位置:三类默认加载器各管一摊
Java 默认有三层类加载器,它们分工明确,互不越界:
-
启动类加载器(Bootstrap ClassLoader):加载
JAVA_HOME/lib下的核心类,比如java.lang.String、java.util.ArrayList。它用 C++ 实现,JVM 内置,你在 Java 代码里拿不到它的引用(调用String.class.getClassLoader()返回null)。 -
扩展类加载器(Extension ClassLoader):加载
JAVA_HOME/lib/ext目录(或java.ext.dirs指定路径)下的 JAR 包,比如早期的javax.*扩展类。它由 Java 实现,可通过ClassLoader.getSystemClassLoader().getParent()获取。 -
应用程序类加载器(AppClassLoader):也叫系统类加载器,加载
-classpath或CLASSPATH环境变量指定路径下的所有类——也就是你写的代码、引入的第三方 JAR(如 log4j、spring-core),都是它加载的。调用ClassLoader.getSystemClassLoader()就能得到它。
看实际输出:用代码验证当前类由谁加载
写一行代码就能立刻看到效果:
System.out.println("String 加载器: " + String.class.getClassLoader());
System.out.println("ArrayList 加载器: " + ArrayList.class.getClassLoader());
System.out.println("自定义类加载器: " + YourClass.class.getClassLoader());
运行后通常会看到:
- 前两行输出
null→ 表明是 Bootstrap 加载的; - 第三行输出类似
sun.misc.Launcher$AppClassLoader@18b4aac2→ 表明是 AppClassLoader 加载的。
看父子关系:所有加载器都遵循双亲委派链
除 Bootstrap 外,每个加载器都有一个父加载器:
- AppClassLoader 的父加载器是 ExtClassLoader;
- ExtClassLoader 的父加载器是 Bootstrap(但返回
null); - 自定义加载器如果没显式指定父加载器,默认父加载器就是 AppClassLoader。
你可以这样验证:
ClassLoader app = ClassLoader.getSystemClassLoader();
System.out.println("App 的父加载器: " + app.getParent());
System.out.println("父的父加载器: " + app.getParent().getParent()); // 输出 null
注意一个常见误区:同一个类被不同加载器加载 = 完全不同的类
哪怕字节码一模一样,只要由不同类加载器加载,JVM 就认为是两个无关的类。比如:
- 你自己写一个
java.lang.String放进 classpath,它不会被加载(因为 Bootstrap 已加载了真正的String); - 如果你用自定义加载器强行加载它,那这个“假 String”和真正的
String不兼容——连赋值都会抛ClassCastException。
这正是双亲委派机制存在的根本原因:保证核心类唯一、安全、可信任。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











