Java基础快速入门: 类加载器

本文纲要

  • 什么是类加载器
  • 类加载的时机
  • 类加载的过程详解
    • 加载
    • 连接(验证 → 准备 → 解析)
    • 初始化
    • 过程小结
  • 类加载器的分类
  • 双亲委派模型
    • 模型原理
    • 代码演示与项目结构
  • 类加载器常用方法
    • 获取系统类加载器
    • 加载资源文件
    • 代码演示与项目结构
  • 总结

类加载器概述

在 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 ) 加载

加载阶段完成三件事:

  1. 通过全限定名获取二进制字节流

    全限定名 = 包名 + 类名(如 com.wb.Student)。不同包下可能重名,因此必须用全限定名精确定位字节码文件。

  2. 将字节流所代表的静态存储结构转化为运行时数据结构

    简单理解:利用上一步得到的流,把字节码文件的内容载入到 JVM 内存中。

  3. 在内存中生成代表该类的 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();

  1. 因为要用到 Student 类,触发加载。
  2. 加载:通过全限定名找到 Student.class,用流读入内存,创建 Student 的 Class 对象(成员信息先用符号引用表示)。
  3. 验证:检查 Student 字节码规范与安全性。
  4. 准备:为静态变量(如 school)分配内存并赋默认值 null。
  5. 解析:把 nameschool 前的符号引用替换成 String 类的直接引用。
  6. 初始化:把 "某大学" 赋值给 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;} 自定义类加载器
系统类加载器
平台类加载器
启动类加载器

双亲委派的核心机制:

  1. 当一个类加载器收到加载请求时,它不会自己先去加载,而是委派给父加载器完成。
  2. 请求层层向上委派,最终到达启动类加载器
  3. 如果父加载器能完成加载(在其负责范围内),就直接返回;如果不能,就交给子加载器尝试加载。

例如,要加载一个用户自定义的类,请求会先传到启动类加载器 → 平台类加载器 → 系统类加载器,最终由系统类加载器在自己负责的 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 运行机制的重要一步,也为后续学习反射、模块化等内容打下坚实基础。