Java的类加载器

什么是类加载器?

Java中的类加载器(ClassLoader)是JVM中用于动态加载类文件的组件。他将 .class文件中的字节码加载到内存中,并将其转换为 Class对象,以供JVM执行。

栈是引用地址,当创建一个对象实例,这个对象名称是存在栈中的,实际上指向的是堆地址

类是一个抽象的,是一个模板

而对象是具体的。是抽象类的实例化

Java中的类加载过程是怎么样的?

类加载指的是 把类加载到JVM中 。把二进制流的形式存储到内存中,之后经过一番解析、处理转换成可用的class类。

二进制流可以来源于class文件,或者通过字节码工具生成的字节码或来自于网络,只要符合格式的二进制流,JVM来着不拒。

类加载流程:

  1. 加载

在加载阶段,类加载器通过给定的类路径查找类的字节码文件,并将其读取到内存中。

  • 类加载器根据类的完全限定名(即包名 + 类名)来查找类文件(例如 com.example.MyClass)。
  • 它会在类路径中查找该类对应的 .class 文件或其他字节码存储位置(如 JAR 包、文件系统、数据库等)。
  • 如果类文件被找到,加载器会将字节码读取到内存中,但并不会立即对其进行初始化。
  1. 连接

连接又可以分为三个阶段:验证、准备、解析

  • 验证:

主要是验证加载进来的二进制流是否符合一定格式,是否规范,是否符合当前 JVM 版本等等之类的验证。

  • 准备:

为静态变量(类变量)赋初始值,也即为它们在方法区划分内存空间。这里注意是静态变量,并且是初始值,比如 int 的初始值是 0。

  • 解析:

解析阶段将类中的符号引用 转换为直接引用 。比如,方法、字段的符号引用会在这一阶段变成内存中的真实地址。

解析是在类被实际使用时才进行的,不一定在类加载时进行。

  1. 初始化

初始化阶段是在类第一次被使用时进行的。它的主要任务是执行类的静态代码块(static 初始化块)和静态变量的初始化。

  • 静态变量初始化:按照定义顺序为静态变量赋值,静态变量的赋值会执行类中的静态初始化代码。
  • 静态代码块:在类初始化时执行,执行类的静态代码块中的内容。

类加载器的作用

  • 动态加载类: 在运行时根据需要加载类,而不是在编译时加载所有类。
  • 隔离不同的类命名空间: 通过不同的类加载器,可以隔离同名类,使得他们不会相互冲突。

类加载器层次结构

JDK8 的时候一共有三种类加载器:

  1. 启动类加载器 (Bootstrap ClassLoader):它属于虚拟机自身的一部分,用C++实现的(JDK9后Java实现),主要负责加载<JAVA_HOME>\lib目录中或被Xbootclasspath指定的路径中的并且文件名是被虚拟机识别的文件,他是所有类加载器的父亲。****
  2. 扩展类加载器(Extension ClassLoader):他是Java实现的,独立于虚拟机,主要负责加载<JAVA_HOME>\lib\ext目录中或被Java.ext.dirs系统变量所指定的路径的类库。
  3. 应用程序类加载器 ( Application ClassLoader ): ,他是Java实现的,独立于虚拟机。主要负责加载用户类(classpath)上的类库,如果我们没有实现自定义的类加载器,那么这个加载器就是我们程序中的默认加载器。

会逐步委托给父类的加载器(双亲委派机制)

1.自定义加载器

2.应用程序加载器

3.扩展类加载器

4.启动类(根)加载器 (委托到这 优先加载

在 JDK9 之后 ,类加载器进行了一些修改,主要是因为 JDK9 引入了模块化 ,即 Jigsaw,原来的 rt.jar、tool.jar 等都被拆成了数十个 jmod 文件,已满足可扩展需求,无需保留 <JAVA_HOME>\lib\ext ,所以扩展类加载器 也被重命名为平台类加载器 (PlatformClassLoader),主要加载被 module-info.java 中定义的类。

且双亲委派的路径也做了一定的变化:

在平台和应用类加载器受到加载请求时,会先判断该类是否属于一个系统模块,如果属于则委派给对应的模块类加载器加载,反之才委派给父类加载器。

双亲委派机制

作用:

双亲委派机制 是 Java 类加载器的核心设计原则。这个机制确保了 Java 类加载器具有层次结构,避免了不同类加载器加载相同类的冲突问题。简而言之,每个类加载器在加载类时都会先委托给它的父类加载器加载,如果父类加载器无法加载这个类,它才会尝试自己加载。

确保类加载器按照从父到子的顺序加载类,优先使用核心类库 (如 rt.jar ),防止类的冲突和覆盖。*

防止程序员故意制造一些bug

比如在应用程序加载类 中重写了String类,而且包名一致java.lang.String 如果没有双亲委派机制的话,这个String就会替换,导致程序运行失败

假设我们在自己的项目中有一个 String类,同时系统中已经有一个名为 String的类存在。由于双亲委派机制,我们的自定义类加载器在加载类时会首先去父类加载器中查找,如果父加载器已经加载过该类,它就不会再次加载,从而避免了类的冲突。

相关推荐
ZZHHWW2 小时前
高性能架构01 -- 开篇
后端·架构
程序员小潘3 小时前
Spring Gateway动态路由实现方案
后端·spring cloud
golang学习记3 小时前
国内完美安装 Rust 环境 + VSCode 编写 Hello World 完整指南(2025 最新)
后端
Undoom3 小时前
解锁超级生产力:手把手教你构建与GitHub深度集成的自动化工作流,让AI成为你的编程副驾驶
后端
我是华为OD~HR~栗栗呀3 小时前
前端面经-高级开发(华为od)
java·前端·后端·python·华为od·华为·面试
菜鸟小九4 小时前
SSM(MybatisPlus)
java·开发语言·spring boot·后端
不爱编程的小九九4 小时前
小九源码-springboot051-智能推荐旅游平台
java·spring boot·后端
数据知道4 小时前
Go基础:常用数学函数处理(主要是math包rand包的处理)
开发语言·后端·golang·go语言
期待のcode4 小时前
MyBatis框架—延迟加载与多级缓存
java·数据库·后端·缓存·mybatis