本文纲要
- 什么是类加载器
- 类加载的时机
- 类加载的过程详解
- 加载
- 连接(验证 → 准备 → 解析)
- 初始化
- 过程小结
- 类加载器的分类
- 双亲委派模型
- 模型原理
- 代码演示与项目结构
- 类加载器常用方法
- 获取系统类加载器
- 加载资源文件
- 代码演示与项目结构
- 总结
类加载器概述
在 Java 中,我们编写的源代码是 .java 文件,经过编译后生成 .class 字节码文件。JVM 实际运行的正是编译后的字节码文件,但字节码文件保存在硬盘上,并不会自动"跑"到内存中执行。
这时就需要一个搬运工,把字节码文件加载到 JVM 内存里,这个搬运工就是类加载器(ClassLoader)。
简单来说,类加载器负责将硬盘上的 .class 文件加载到内存中,供 JVM 使用。
类加载的时机
类并不是在 JVM 启动时一次性全部加载的,而是用到的时候才加载,不用不加载。
具体来说,有六种情况会触发类加载:
| 触发条件 | 说明 |
|---|---|
创建类的实例(new 对象) |
如 new Student() 时会加载 Student 类 |
| 调用类的静态方法 | 静态方法通过类名直接调用,不需要实例,如 Student.study() |
| 访问类的静态变量或为其赋值 | 静态变量也是类级别的,访问时需加载类 |
使用反射方式强制创建类的 Class 对象 |
反射(后续章节会学)会触发类加载 |
| 初始化某个类的子类 | 先加载父类再加载子类,比如创建 Student 对象时,会先加载其父类 |
| 直接使用 java 命令运行主类 | 运行一个类时必然要加载该类 |
类加载的过程详解
类的加载并非一瞬间完成,而是经历一个加载 → 连接 → 初始化的过程。
其中连接阶段又细分为验证、准备、解析三个步骤。
用一张流程图概览:
#mermaid-svg-zNJKNTurDctttUkk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zNJKNTurDctttUkk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zNJKNTurDctttUkk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zNJKNTurDctttUkk .error-icon{fill:#552222;}#mermaid-svg-zNJKNTurDctttUkk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zNJKNTurDctttUkk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zNJKNTurDctttUkk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zNJKNTurDctttUkk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zNJKNTurDctttUkk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zNJKNTurDctttUkk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zNJKNTurDctttUkk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zNJKNTurDctttUkk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zNJKNTurDctttUkk .marker.cross{stroke:#333333;}#mermaid-svg-zNJKNTurDctttUkk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zNJKNTurDctttUkk p{margin:0;}#mermaid-svg-zNJKNTurDctttUkk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zNJKNTurDctttUkk .cluster-label text{fill:#333;}#mermaid-svg-zNJKNTurDctttUkk .cluster-label span{color:#333;}#mermaid-svg-zNJKNTurDctttUkk .cluster-label span p{background-color:transparent;}#mermaid-svg-zNJKNTurDctttUkk .label text,#mermaid-svg-zNJKNTurDctttUkk span{fill:#333;color:#333;}#mermaid-svg-zNJKNTurDctttUkk .node rect,#mermaid-svg-zNJKNTurDctttUkk .node circle,#mermaid-svg-zNJKNTurDctttUkk .node ellipse,#mermaid-svg-zNJKNTurDctttUkk .node polygon,#mermaid-svg-zNJKNTurDctttUkk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zNJKNTurDctttUkk .rough-node .label text,#mermaid-svg-zNJKNTurDctttUkk .node .label text,#mermaid-svg-zNJKNTurDctttUkk .image-shape .label,#mermaid-svg-zNJKNTurDctttUkk .icon-shape .label{text-anchor:middle;}#mermaid-svg-zNJKNTurDctttUkk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zNJKNTurDctttUkk .rough-node .label,#mermaid-svg-zNJKNTurDctttUkk .node .label,#mermaid-svg-zNJKNTurDctttUkk .image-shape .label,#mermaid-svg-zNJKNTurDctttUkk .icon-shape .label{text-align:center;}#mermaid-svg-zNJKNTurDctttUkk .node.clickable{cursor:pointer;}#mermaid-svg-zNJKNTurDctttUkk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zNJKNTurDctttUkk .arrowheadPath{fill:#333333;}#mermaid-svg-zNJKNTurDctttUkk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zNJKNTurDctttUkk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zNJKNTurDctttUkk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zNJKNTurDctttUkk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zNJKNTurDctttUkk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zNJKNTurDctttUkk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zNJKNTurDctttUkk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zNJKNTurDctttUkk .cluster text{fill:#333;}#mermaid-svg-zNJKNTurDctttUkk .cluster span{color:#333;}#mermaid-svg-zNJKNTurDctttUkk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zNJKNTurDctttUkk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zNJKNTurDctttUkk rect.text{fill:none;stroke-width:0;}#mermaid-svg-zNJKNTurDctttUkk .icon-shape,#mermaid-svg-zNJKNTurDctttUkk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zNJKNTurDctttUkk .icon-shape p,#mermaid-svg-zNJKNTurDctttUkk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zNJKNTurDctttUkk .icon-shape .label rect,#mermaid-svg-zNJKNTurDctttUkk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zNJKNTurDctttUkk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zNJKNTurDctttUkk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zNJKNTurDctttUkk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 连接阶段
加载
验证
准备
解析
初始化
1 ) 加载
加载阶段完成三件事:
-
通过全限定名获取二进制字节流
全限定名 = 包名 + 类名(如 com.wb.Student)。不同包下可能重名,因此必须用全限定名精确定位字节码文件。
-
将字节流所代表的静态存储结构转化为运行时数据结构
简单理解:利用上一步得到的流,把字节码文件的内容载入到 JVM 内存中。
-
在内存中生成代表该类的 Class 对象
任何被使用的类,JVM 都会为它创建一个 Class 对象,用来存储该类的成员信息(变量、方法等)。
用一张图表示:
内存(JVM) 硬盘 内存(JVM) 硬盘 #mermaid-svg-ns898w65XV0cNArS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ns898w65XV0cNArS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ns898w65XV0cNArS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ns898w65XV0cNArS .error-icon{fill:#552222;}#mermaid-svg-ns898w65XV0cNArS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ns898w65XV0cNArS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ns898w65XV0cNArS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ns898w65XV0cNArS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ns898w65XV0cNArS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ns898w65XV0cNArS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ns898w65XV0cNArS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ns898w65XV0cNArS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ns898w65XV0cNArS .marker.cross{stroke:#333333;}#mermaid-svg-ns898w65XV0cNArS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ns898w65XV0cNArS p{margin:0;}#mermaid-svg-ns898w65XV0cNArS .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ns898w65XV0cNArS text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ns898w65XV0cNArS .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ns898w65XV0cNArS .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ns898w65XV0cNArS .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ns898w65XV0cNArS .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ns898w65XV0cNArS #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ns898w65XV0cNArS .sequenceNumber{fill:white;}#mermaid-svg-ns898w65XV0cNArS #sequencenumber{fill:#333;}#mermaid-svg-ns898w65XV0cNArS #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ns898w65XV0cNArS .messageText{fill:#333;stroke:none;}#mermaid-svg-ns898w65XV0cNArS .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ns898w65XV0cNArS .labelText,#mermaid-svg-ns898w65XV0cNArS .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ns898w65XV0cNArS .loopText,#mermaid-svg-ns898w65XV0cNArS .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ns898w65XV0cNArS .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ns898w65XV0cNArS .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ns898w65XV0cNArS .noteText,#mermaid-svg-ns898w65XV0cNArS .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ns898w65XV0cNArS .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ns898w65XV0cNArS .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ns898w65XV0cNArS .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ns898w65XV0cNArS .actorPopupMenu{position:absolute;}#mermaid-svg-ns898w65XV0cNArS .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ns898w65XV0cNArS .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ns898w65XV0cNArS .actor-man circle,#mermaid-svg-ns898w65XV0cNArS line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ns898w65XV0cNArS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} class 文件 ① 通过全限定名找到 class 文件② 通过流将字节码载入内存③ 创建 Class 对象存储成员信息
以 Student 为例,内存中会生成一个 Student 的 Class 对象,其内部包含 name、age 等成员变量以及 study、sleep 等方法的信息。
2 ) 连接(验证 → 准备 → 解析)
2.1 验证
校验字节码文件是否符合 JVM 规范,有无安全隐患,确保不会危害虚拟机自身安全。
2.2 准备
为类的静态变量(类变量)分配内存并设置默认初始化值。
例如:static String school; 此时会在 Class 对象中为 school 分一块空间,并赋值为 null(引用类型的默认值)。
2.3 解析
将类二进制数据中的符号引用替换为直接引用。
为什么会有符号引用?
当我们加载 Student 类时,它的成员可能使用其他类,例如 String name;,这里的 String 是一个引用类型,而在最初加载 Student 时,String 类可能还没被加载或不知道具体位置,于是先用一个"符号"占位,这就叫符号引用。
等到解析阶段,JVM 会真正去查找 String 类的 Class 对象在内存中的地址,然后把占位符号替换成实际的地址(直接引用)。这样一来,后续访问就能直接定位了。
可以用下图理解:
#mermaid-svg-22o5kiK0ec9NFBk3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-22o5kiK0ec9NFBk3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-22o5kiK0ec9NFBk3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-22o5kiK0ec9NFBk3 .error-icon{fill:#552222;}#mermaid-svg-22o5kiK0ec9NFBk3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-22o5kiK0ec9NFBk3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-22o5kiK0ec9NFBk3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-22o5kiK0ec9NFBk3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-22o5kiK0ec9NFBk3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-22o5kiK0ec9NFBk3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-22o5kiK0ec9NFBk3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-22o5kiK0ec9NFBk3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-22o5kiK0ec9NFBk3 .marker.cross{stroke:#333333;}#mermaid-svg-22o5kiK0ec9NFBk3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-22o5kiK0ec9NFBk3 p{margin:0;}#mermaid-svg-22o5kiK0ec9NFBk3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-22o5kiK0ec9NFBk3 .cluster-label text{fill:#333;}#mermaid-svg-22o5kiK0ec9NFBk3 .cluster-label span{color:#333;}#mermaid-svg-22o5kiK0ec9NFBk3 .cluster-label span p{background-color:transparent;}#mermaid-svg-22o5kiK0ec9NFBk3 .label text,#mermaid-svg-22o5kiK0ec9NFBk3 span{fill:#333;color:#333;}#mermaid-svg-22o5kiK0ec9NFBk3 .node rect,#mermaid-svg-22o5kiK0ec9NFBk3 .node circle,#mermaid-svg-22o5kiK0ec9NFBk3 .node ellipse,#mermaid-svg-22o5kiK0ec9NFBk3 .node polygon,#mermaid-svg-22o5kiK0ec9NFBk3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-22o5kiK0ec9NFBk3 .rough-node .label text,#mermaid-svg-22o5kiK0ec9NFBk3 .node .label text,#mermaid-svg-22o5kiK0ec9NFBk3 .image-shape .label,#mermaid-svg-22o5kiK0ec9NFBk3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-22o5kiK0ec9NFBk3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-22o5kiK0ec9NFBk3 .rough-node .label,#mermaid-svg-22o5kiK0ec9NFBk3 .node .label,#mermaid-svg-22o5kiK0ec9NFBk3 .image-shape .label,#mermaid-svg-22o5kiK0ec9NFBk3 .icon-shape .label{text-align:center;}#mermaid-svg-22o5kiK0ec9NFBk3 .node.clickable{cursor:pointer;}#mermaid-svg-22o5kiK0ec9NFBk3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-22o5kiK0ec9NFBk3 .arrowheadPath{fill:#333333;}#mermaid-svg-22o5kiK0ec9NFBk3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-22o5kiK0ec9NFBk3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-22o5kiK0ec9NFBk3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-22o5kiK0ec9NFBk3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-22o5kiK0ec9NFBk3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-22o5kiK0ec9NFBk3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-22o5kiK0ec9NFBk3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-22o5kiK0ec9NFBk3 .cluster text{fill:#333;}#mermaid-svg-22o5kiK0ec9NFBk3 .cluster span{color:#333;}#mermaid-svg-22o5kiK0ec9NFBk3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-22o5kiK0ec9NFBk3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-22o5kiK0ec9NFBk3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-22o5kiK0ec9NFBk3 .icon-shape,#mermaid-svg-22o5kiK0ec9NFBk3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-22o5kiK0ec9NFBk3 .icon-shape p,#mermaid-svg-22o5kiK0ec9NFBk3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-22o5kiK0ec9NFBk3 .icon-shape .label rect,#mermaid-svg-22o5kiK0ec9NFBk3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-22o5kiK0ec9NFBk3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-22o5kiK0ec9NFBk3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-22o5kiK0ec9NFBk3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 解析后
加载时
解析过程查找替换
Student Class对象
name : 符号引用 (→ String)
Student Class对象
name : 直接引用 (String的Class地址)
3 ) 初始化
初始化是根据程序员的意图为静态变量显式赋值并执行静态代码块。
例如: static String school = "某大学";
在准备阶段 school 被初始化为 null,到了初始化阶段,school 才被赋值为 "某大学",覆盖默认值。
4 ) 过程小结
以一个具体的例子串起整个类加载过程:执行 Student s = new Student();
- 因为要用到 Student 类,触发加载。
- 加载:通过全限定名找到 Student.class,用流读入内存,创建
Student的 Class 对象(成员信息先用符号引用表示)。 - 验证:检查
Student字节码规范与安全性。 - 准备:为静态变量(如
school)分配内存并赋默认值 null。 - 解析:把
name和school前的符号引用替换成 String 类的直接引用。 - 初始化:把 "某大学" 赋值给 school。
类加载器的分类
大部分 Java 程序会用到以下三种系统提供的类加载器:
- 启动类加载器(
Bootstrap ClassLoader)
虚拟机内置,由 C++ 实现,加载核心 Java 类(如 rt.jar)。它在 JVM 启动时自动运行,是最顶层的加载器,在 Java 代码中一般用 null 表示。 - 平台类加载器(
Platform ClassLoader)
负责加载 JDK 内部的一些特殊模块(如 java.sql、java.xml 等)。JDK 9 之后取代了原来的"扩展类加载器"。 - 系统类加载器(
System / App ClassLoader)
也叫应用程序类加载器,负责加载用户类路径(classpath)上的类,我们自己编写的类通常都由它加载。
三种加载器的层次关系:
#mermaid-svg-pdDtuJB8VNwk5oSZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pdDtuJB8VNwk5oSZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pdDtuJB8VNwk5oSZ .error-icon{fill:#552222;}#mermaid-svg-pdDtuJB8VNwk5oSZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pdDtuJB8VNwk5oSZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pdDtuJB8VNwk5oSZ .marker.cross{stroke:#333333;}#mermaid-svg-pdDtuJB8VNwk5oSZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pdDtuJB8VNwk5oSZ p{margin:0;}#mermaid-svg-pdDtuJB8VNwk5oSZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pdDtuJB8VNwk5oSZ .cluster-label text{fill:#333;}#mermaid-svg-pdDtuJB8VNwk5oSZ .cluster-label span{color:#333;}#mermaid-svg-pdDtuJB8VNwk5oSZ .cluster-label span p{background-color:transparent;}#mermaid-svg-pdDtuJB8VNwk5oSZ .label text,#mermaid-svg-pdDtuJB8VNwk5oSZ span{fill:#333;color:#333;}#mermaid-svg-pdDtuJB8VNwk5oSZ .node rect,#mermaid-svg-pdDtuJB8VNwk5oSZ .node circle,#mermaid-svg-pdDtuJB8VNwk5oSZ .node ellipse,#mermaid-svg-pdDtuJB8VNwk5oSZ .node polygon,#mermaid-svg-pdDtuJB8VNwk5oSZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pdDtuJB8VNwk5oSZ .rough-node .label text,#mermaid-svg-pdDtuJB8VNwk5oSZ .node .label text,#mermaid-svg-pdDtuJB8VNwk5oSZ .image-shape .label,#mermaid-svg-pdDtuJB8VNwk5oSZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-pdDtuJB8VNwk5oSZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pdDtuJB8VNwk5oSZ .rough-node .label,#mermaid-svg-pdDtuJB8VNwk5oSZ .node .label,#mermaid-svg-pdDtuJB8VNwk5oSZ .image-shape .label,#mermaid-svg-pdDtuJB8VNwk5oSZ .icon-shape .label{text-align:center;}#mermaid-svg-pdDtuJB8VNwk5oSZ .node.clickable{cursor:pointer;}#mermaid-svg-pdDtuJB8VNwk5oSZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pdDtuJB8VNwk5oSZ .arrowheadPath{fill:#333333;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pdDtuJB8VNwk5oSZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pdDtuJB8VNwk5oSZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pdDtuJB8VNwk5oSZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pdDtuJB8VNwk5oSZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pdDtuJB8VNwk5oSZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pdDtuJB8VNwk5oSZ .cluster text{fill:#333;}#mermaid-svg-pdDtuJB8VNwk5oSZ .cluster span{color:#333;}#mermaid-svg-pdDtuJB8VNwk5oSZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pdDtuJB8VNwk5oSZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pdDtuJB8VNwk5oSZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-pdDtuJB8VNwk5oSZ .icon-shape,#mermaid-svg-pdDtuJB8VNwk5oSZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pdDtuJB8VNwk5oSZ .icon-shape p,#mermaid-svg-pdDtuJB8VNwk5oSZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pdDtuJB8VNwk5oSZ .icon-shape .label rect,#mermaid-svg-pdDtuJB8VNwk5oSZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pdDtuJB8VNwk5oSZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pdDtuJB8VNwk5oSZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pdDtuJB8VNwk5oSZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Bootstrap ClassLoader
启动类加载器
Platform ClassLoader
平台类加载器
System ClassLoader
系统类加载器
双亲委派模型
1 ) 模型原理
类加载器的层级关系形成了双亲委派模型 。这个模型规定:除了顶层的启动类加载器之外,其他加载器都应该有自己的父加载器(逻辑上的父子关系,而非代码中的 extends 继承)。
#mermaid-svg-BCljWRYTVY4BsYzE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BCljWRYTVY4BsYzE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BCljWRYTVY4BsYzE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BCljWRYTVY4BsYzE .error-icon{fill:#552222;}#mermaid-svg-BCljWRYTVY4BsYzE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BCljWRYTVY4BsYzE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BCljWRYTVY4BsYzE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BCljWRYTVY4BsYzE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BCljWRYTVY4BsYzE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BCljWRYTVY4BsYzE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BCljWRYTVY4BsYzE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BCljWRYTVY4BsYzE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BCljWRYTVY4BsYzE .marker.cross{stroke:#333333;}#mermaid-svg-BCljWRYTVY4BsYzE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BCljWRYTVY4BsYzE p{margin:0;}#mermaid-svg-BCljWRYTVY4BsYzE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BCljWRYTVY4BsYzE .cluster-label text{fill:#333;}#mermaid-svg-BCljWRYTVY4BsYzE .cluster-label span{color:#333;}#mermaid-svg-BCljWRYTVY4BsYzE .cluster-label span p{background-color:transparent;}#mermaid-svg-BCljWRYTVY4BsYzE .label text,#mermaid-svg-BCljWRYTVY4BsYzE span{fill:#333;color:#333;}#mermaid-svg-BCljWRYTVY4BsYzE .node rect,#mermaid-svg-BCljWRYTVY4BsYzE .node circle,#mermaid-svg-BCljWRYTVY4BsYzE .node ellipse,#mermaid-svg-BCljWRYTVY4BsYzE .node polygon,#mermaid-svg-BCljWRYTVY4BsYzE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BCljWRYTVY4BsYzE .rough-node .label text,#mermaid-svg-BCljWRYTVY4BsYzE .node .label text,#mermaid-svg-BCljWRYTVY4BsYzE .image-shape .label,#mermaid-svg-BCljWRYTVY4BsYzE .icon-shape .label{text-anchor:middle;}#mermaid-svg-BCljWRYTVY4BsYzE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BCljWRYTVY4BsYzE .rough-node .label,#mermaid-svg-BCljWRYTVY4BsYzE .node .label,#mermaid-svg-BCljWRYTVY4BsYzE .image-shape .label,#mermaid-svg-BCljWRYTVY4BsYzE .icon-shape .label{text-align:center;}#mermaid-svg-BCljWRYTVY4BsYzE .node.clickable{cursor:pointer;}#mermaid-svg-BCljWRYTVY4BsYzE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BCljWRYTVY4BsYzE .arrowheadPath{fill:#333333;}#mermaid-svg-BCljWRYTVY4BsYzE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BCljWRYTVY4BsYzE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BCljWRYTVY4BsYzE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BCljWRYTVY4BsYzE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BCljWRYTVY4BsYzE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BCljWRYTVY4BsYzE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BCljWRYTVY4BsYzE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BCljWRYTVY4BsYzE .cluster text{fill:#333;}#mermaid-svg-BCljWRYTVY4BsYzE .cluster span{color:#333;}#mermaid-svg-BCljWRYTVY4BsYzE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BCljWRYTVY4BsYzE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BCljWRYTVY4BsYzE rect.text{fill:none;stroke-width:0;}#mermaid-svg-BCljWRYTVY4BsYzE .icon-shape,#mermaid-svg-BCljWRYTVY4BsYzE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BCljWRYTVY4BsYzE .icon-shape p,#mermaid-svg-BCljWRYTVY4BsYzE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BCljWRYTVY4BsYzE .icon-shape .label rect,#mermaid-svg-BCljWRYTVY4BsYzE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BCljWRYTVY4BsYzE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BCljWRYTVY4BsYzE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BCljWRYTVY4BsYzE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 自定义类加载器
系统类加载器
平台类加载器
启动类加载器
双亲委派的核心机制:
- 当一个类加载器收到加载请求时,它不会自己先去加载,而是委派给父加载器完成。
- 请求层层向上委派,最终到达启动类加载器。
- 如果父加载器能完成加载(在其负责范围内),就直接返回;如果不能,就交给子加载器尝试加载。
例如,要加载一个用户自定义的类,请求会先传到启动类加载器 → 平台类加载器 → 系统类加载器,最终由系统类加载器在自己负责的 classpath 中找到并加载。
通过这种方式,保证了核心 Java 类的安全(比如 java.lang.Object 始终由启动类加载器加载,防止被篡改),也避免了类的重复加载。
2 ) 代码演示与项目结构
以下代码通过 ClassLoader.getSystemClassLoader() 获取系统类加载器,再逐级获取父加载器,打印后即可看到层次关系。
项目结构:
dir
myclassloader/
└── src/
└── com/wb/myclassloader/
└── ClassLoaderDemo1.java
java
package com.wb.myclassloader;
/*
演示双亲委派模型 ------ 获取各级类加载器
*/
public class ClassLoaderDemo1 {
public static void main(String[] args) {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// 获取系统类加载器的父加载器 ------ 平台类加载器
ClassLoader classLoader1 = systemClassLoader.getParent();
// 获取平台类加载器的父加载器 ------ 启动类加载器
ClassLoader classLoader2 = classLoader1.getParent();
// 打印结果
System.out.println("系统类加载器:" + systemClassLoader);
System.out.println("平台类加载器:" + classLoader1);
System.out.println("启动类加载器:" + classLoader2);
}
}
运行结果:
log
系统类加载器:jdk.internal.loader.ClassLoaders$AppClassLoader@...
平台类加载器:jdk.internal.loader.ClassLoaders$PlatformClassLoader@...
启动类加载器:null
可以看到,启动类加载器由 C++ 实现,在 Java 层无法获取到具体对象,因此输出 null。
类加载器常用方法
ClassLoader 类中提供了几个重要的方法,需要掌握:
| 方法 | 说明 |
|---|---|
static ClassLoader getSystemClassLoader() |
返回系统类加载器 |
InputStream getResourceAsStream(String name) |
使用当前类加载器加载资源文件,返回字节输入流 |
其中 getResourceAsStream 的参数是相对路径 ,对于系统类加载器而言,相对路径的根目录通常是 src 文件夹(类路径的根)。因此资源文件需要放在 src 目录下才能被正确加载。
1 ) 获取系统类加载器
java
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
该方法是一个静态方法,可以直接通过类名调用,获取负责加载用户类路径的类加载器。
2 ) 加载资源文件
结合 Properties 类,我们可以方便地加载配置文件。假设我们需要读取 prop.properties 文件中的配置信息。
3 ) 代码演示与项目结构
项目结构:
dir
myclassloader/
└── src/
├── com/wb/myclassloader/
│ └── ClassLoaderDemo2.java
└── prop.properties
prop.properties 内容:
properties
properties
name=zhangsan
age=13
ClassLoaderDemo2.java 内容:
java
package com.wb.myclassloader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/*
演示使用类加载器加载资源文件
*/
public class ClassLoaderDemo2 {
public static void main(String[] args) throws IOException {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// 利用加载器加载指定的资源文件
// 参数:相对于 src 目录的文件路径
// 返回值:字节流
InputStream is = systemClassLoader.getResourceAsStream("prop.properties");
// 将流中数据读取到 Properties 集合中
Properties prop = new Properties();
prop.load(is);
// 打印读取到的数据
System.out.println(prop);
// 释放资源
is.close();
}
}
运行程序,控制台输出:
log
{age=13, name=zhangsan}
关键点:
getResourceAsStream 的参数路径是相对于类路径(classpath)的,在标准项目结构下,src 目录就是类路径的根,所以文件直接放在 src 下即可。
实际开发中,我们常使用这种方式来加载 .properties 配置文件。
总结
- 类加载器负责把字节码文件从硬盘加载到 JVM 内存。
- 类加载遵循用到才加载的原则,共有六种触发时机。
- 加载过程包括加载 → 验证 → 准备 → 解析 → 初始化,其中验证、准备、解析合称连接。
- Java 主要提供三种类加载器:启动类、平台类、系统类,它们通过双亲委派模型协同工作,保证安全性并避免重复加载。
- 我们可以通过
ClassLoader的静态方法获取系统类加载器,并利用它加载资源文件。
掌握类加载器的基本概念和常用 API,是理解 Java 运行机制的重要一步,也为后续学习反射、模块化等内容打下坚实基础。