Java_类加载器

news/2025/2/4 5:57:54/

小程一言

本专栏是对Java知识点的总结。在学习Java的过程中,学习的笔记,加入自己的思考,结合各种资料的整理。

文章与程序一样,一定都是不完美的,因为不完美,才拥有不断追求完美的动力

类加载器的基础

首先要明确类加载器的职责就是加载类。当JVM启动时,系统会使用类加载器将必要的类(如java.lang.*类库)加载到内存中。Java中的类加载器是递归结构,遵循“双亲委派模型”。

双亲委派模型

核心思想

当一个类加载器接到加载类的请求时,它不会立即去加载该类,而是先将请求委派给它的父类加载器。只有当父类加载器无法完成加载时,子类加载器才会尝试自己加载。

优势

它保证了Java的核心类库(如java.lang.String)始终由系统的启动类加载器来加载,避免了“重复加载”问题,并增加了类加载的安全性。

双亲委派模型的加载顺序:

  1. 启动类加载器
  2. 扩展类加载器
  3. 系统类加载器
  4. 自定义类加载器
    在这里插入图片描述

类加载器的职责

  • 启动类加载器(Bootstrap ClassLoader):负责加载JVM的核心类库,如java.lang.*包中的类。它是Java中最底层的类加载器,通常由C或C++编写,并且由JVM本身提供。

  • 扩展类加载器(Extension ClassLoader):负责加载jre/lib/ext目录下的类或JVM扩展目录中的类。它的父类是启动类加载器

  • 系统类加载器(System ClassLoader):负责加载应用程序的类路径(Classpath)中指定的类。它通常是由用户定义的类所在的目录、JAR包等。

  • 自定义类加载器(Custom ClassLoader):由开发者自定义的类加载器,可以扩展现有的类加载器,来加载类文件、网络上的类、数据库中的类等。

类加载器的工作流程

类加载器的工作流程可以简单分为以下几个步骤:

  1. 加载类加载器根据类名,查找并读取相应的字节码文件(.class文件)。
  2. 验证:JVM会验证字节码文件的有效性,确保它符合Java的语言规范。
  3. 准备:为类的静态字段分配内存,并为它们赋默认值(如null0等)。
  4. 解析:将类中的符号引用转换为直接引用,主要是指将常量池中的符号,如类名、字段名、方法名等,解析为内存地址。
  5. 初始化:执行类的静态初始化代码(如静态变量的初始化和静态块的执行)。
    在这里插入图片描述

举例:如何在Java中使用类加载器

启动类加载器、扩展类加载器与系统类加载器

java">public class ClassLoaderExample {public static void main(String[] args) {// 获取当前类加载器ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println("系统类加载器:" + systemClassLoader);// 获取扩展类加载器ClassLoader extClassLoader = systemClassLoader.getParent();System.out.println("扩展类加载器:" + extClassLoader);// 获取启动类加载器ClassLoader bootstrapClassLoader = extClassLoader.getParent();System.out.println("启动类加载器:" + bootstrapClassLoader);}
}

输出

系统类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
扩展类加载器:sun.misc.Launcher$ExtClassLoader@5e91993f
启动类加载器:null

解释

自定义类加载器

通过继承ClassLoader类来实现自定义类加载器。假设我们有一个自定义的MyClassLoader,它从特定的目录加载类:

java">import java.io.*;
public class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {String path = classPath + name.replace('.', File.separatorChar) + ".class";File file = new File(path);if (file.exists()) {try (FileInputStream inputStream = new FileInputStream(file)) {byte[] bytes = new byte[(int) file.length()];inputStream.read(bytes);return defineClass(name, bytes, 0, bytes.length);} catch (IOException e) {e.printStackTrace();}}return super.findClass(name);}public static void main(String[] args) throws Exception {MyClassLoader myClassLoader = new MyClassLoader("path/to/classes/");// 使用自定义类加载器加载类Class<?> clazz = myClassLoader.loadClass("com.example.MyClass");System.out.println("加载的类: " + clazz.getName());}
}

本例中,定义了一个MyClassLoader,它从指定的路径加载类文件。当你调用loadClass()时,它会尝试使用findClass()方法加载类。如果找不到,系统类加载器会被调用。

例如,在使用自定义类加载器加载完类后,如果不再需要这些类,类加载器本身也会被垃圾回收,类将被卸载。类卸载通常发生在类加载器不再有任何引用,并且类加载器本身被回收时。
在这里插入图片描述

类加载器与类冲突

有时会遇到不同的类加载器加载同一个类的情况。如下:

java">public class TestClass {public void printMessage() {System.out.println("Hello from TestClass!");}public static void main(String[] args) throws Exception {MyClassLoader myClassLoader1 = new MyClassLoader("path/to/classes/");MyClassLoader myClassLoader2 = new MyClassLoader("path/to/classes/");Class<?> clazz1 = myClassLoader1.loadClass("TestClass");Class<?> clazz2 = myClassLoader2.loadClass("TestClass");System.out.println("clazz1 == clazz2: " + (clazz1 == clazz2));}
}

在这个例子中,我们创建了两个不同的MyClassLoader,并用它们分别加载TestClass类。由于每个MyClassLoader加载的是不同的TestClass实例,clazz1 == clazz2将会输出false,表明它们是不同的类。
在这里插入图片描述

总结

  • Java的类加载器机制确保了类的加载、验证、链接和初始化的顺序。
  • 类加载器采用双亲委派模型,从而保证了系统类库的统一加载,避免了类的重复加载。
  • 你可以通过继承ClassLoader类来自定义类加载器,控制类的加载过程。
  • 类加载器的生命周期与JVM的生命周期相匹配,因此类会在合适的时候被卸载,防止内存泄漏。

http://www.ppmy.cn/news/1569160.html

相关文章

Visual Studio Code应用本地部署的deepseek

1.打开Visual Studio Code&#xff0c;在插件中搜索continue&#xff0c;安装插件。 2.添加新的大语言模型&#xff0c;我们选择ollama. 3.直接点connect&#xff0c;会链接本地下载好的deepseek模型。 参看上篇文章&#xff1a;deepseek本地部署-CSDN博客 4.输入需求生成可用…

Chromium132 编译指南 - Android 篇(一):编译前准备

1. 引言 欢迎来到《Chromium 132 编译指南 - Android 篇》系列的第一部分。本系列指南将引导您逐步完成在 Android 平台上编译 Chromium 132 版本的全过程。Chromium 作为一款由 Google 主导开发的开源浏览器引擎&#xff0c;为众多现代浏览器提供了核心驱动力。而 Android 作…

C语言教学第三课:运算符与表达式

一、课程导入 同学们&#xff0c;上节课我们学习了变量和数据类型&#xff0c;这些是C语言的基础。今天&#xff0c;我们将继续深入学习C语言中的运算符与表达式。运算符是C语言中用于执行各种操作的符号&#xff0c;而表达式则是由变量、常量和运算符组成的有意义的组合。通过…

什么是线性化PDF?

线性化PDF是一种特殊的PDF文件组织方式。 总体而言&#xff0c;PDF是一种极为优雅且设计精良的格式。PDF由大量PDF对象构成&#xff0c;这些对象用于创建页面。相关信息存储在一棵二叉树中&#xff0c;该二叉树同时记录文件中每个对象的位置。因此&#xff0c;打开文件时只需加…

Java篇之继承

目录 一. 继承 1. 为什么需要继承 2. 继承的概念 3. 继承的语法 4. 访问父类成员 4.1 子类中访问父类的成员变量 4.2 子类中访问父类的成员方法 5. super关键字 6. super和this关键字 7. 子类构造方法 8. 代码块的执行顺序 9. protected访问修饰限定符 10. 继承方式…

《STL基础之vector、list、deque》

【vector、list、deque导读】vector、list、deque这三种序列式的容器&#xff0c;算是比较的基础容器&#xff0c;也是大家在日常开发中常用到的容器&#xff0c;因为底层用到的数据结构比较简单&#xff0c;笔者就将他们三者放到一起做下对比分析&#xff0c;介绍下基本用法&a…

Baklib在内容中台智能化推荐系统中的应用与未来发展路径分析

内容概要 在当今数字化快速发展的时代&#xff0c;内容分发的方式与技术日益重要。内容中台智能化推荐系统通过精细的数据分析与用户行为研究&#xff0c;能够有效提升内容分发的精准度与效率。本文旨在深入探讨Baklib的应用&#xff0c;分析其在内容中台中的技术优势及实际实…

Oracle Primavera P6 最新版 v24.12 更新 2/2

目录 一. 引言 二. P6 EPPM 更新内容 1. 用户管理改进 2. 更轻松地标准化用户设置 3. 摘要栏标签汇总数据字段 4. 将里程碑和剩余最早开始日期拖到甘特图上 5. 轻松访问审计数据 6. 粘贴数据时排除安全代码 7. 改进了状态更新卡片视图中的筛选功能 8. 直接从活动电子…