当前位置: 首页 > backend >正文

类与类加载器

在Java中,类和类加载器是密切相关的两个概念,理解它们有助于我们更好地掌握Java的运行机制。

什么是Java类?

Java类就像是一个模板或蓝图,它定义了对象的属性和行为。比如"汽车"可以看作一个类,它有颜色、品牌等属性,有行驶、刹车等行为。我们根据这个类可以创建具体的汽车对象(如一辆红色的特斯拉)。

// 汽车类(模板)
class Car {String color;  // 属性String brand;  // 属性// 行为void drive() {System.out.println(brand + "汽车正在行驶");}
}// 创建对象(根据模板造具体的车)
Car myCar = new Car();
myCar.color = "红色";
myCar.brand = "特斯拉";
myCar.drive();  // 输出:特斯拉汽车正在行驶

什么是类加载器?

类加载器(ClassLoader)就像是"类的搬运工",它的作用是把.class文件(编译后的类)加载到Java虚拟机(JVM)中,让JVM能够认识这个类并使用它。

想象一下:JVM就像一个大型工厂,类就像是生产所需的图纸。类加载器的工作就是把这些图纸从文件系统、网络或其他地方运送到工厂里,供工厂使用。

咱们可以把Java虚拟机(JVM)想象成一个“大工厂”,类加载器就是负责给这个工厂“运图纸(类)”的工人。

从虚拟机角度看(两种类加载器)

  • 启动类加载器(Bootstrap ClassLoader):它是工厂里“最核心的元老级搬运工”,用C++写的,是虚拟机本身的一部分。专门搬最基础、最核心的“工厂自带图纸”,像java.lang.String这类Java最根本的类,就靠它搬。
  • 其他类加载器:这些是“后来的搬运工”,用Java写的,和虚拟机是分开的。它们都得继承java.lang.ClassLoader这个抽象类,负责搬一些额外的图纸。

从开发者角度看(更细致的划分)

  • 启动类加载器:还是那个“核心元老搬运工”,负责搬<JRE_HOME>\lib目录里(或者-Xbootclasspath参数指定路径里)、虚拟机能认出来的“核心图纸”。比如java.util(工具类)、java.io(输入输出类)、java.lang(语言基础类)这些常用基础类库。而且它很“傲娇”,只认文件名,像rt.jar这类符合名字的才搬,名字不对的,哪怕在lib目录里也不管。另外,Java程序没法直接调用它。

  • 扩展类加载器(Extension ClassLoader):它是“扩展搬运工”,负责搬<JRE_HOME>/lib/ext目录或者java.ext.dir系统变量指定路径里的“扩展图纸”。像swing(界面相关)、内置js引擎、xml解析器这些以javax开头的扩展类库,都由它来搬。开发者是可以直接用这个加载器的。

  • 应用程序类加载器(Application ClassLoader):也叫“系统类加载器”,是“用户搬运工”。负责搬用户自己指定的类路径(ClassPath)里的“图纸”,比如我们自己写的类,或者第三方的jar包。如果我们没自己定义类加载器,程序默认就用它来搬图纸。而且它可以通过ClassLoadergetSystemClassLoader()方法获取到,开发者能直接使用。

类与类加载器的关系

  • 每个类被加载到JVM后,都会记录是被哪个类加载器加载的
  • 类加载器之间存在"父子关系":Application的父是Extension,Extension的父是Bootstrap
  • 判断两个类是否相同,不仅要看类名是否一样,还要看它们是否被同一个类加载器加载

打个比方:两个名字相同的"汽车"图纸,如果一个是由"中国搬运工"搬来的,一个是由"美国搬运工"搬来的,JVM会认为它们是不同的类。

为什么需要类加载器?

  1. 实现了类的动态加载:需要用某个类时才加载,不用时不加载,节省资源
  2. 实现了隔离性:不同的类加载器可以加载同名类而不冲突,这对容器(如Tomcat)很重要
  3. 安全性:可以通过自定义类加载器来控制哪些类能被加载,防止恶意代码

理解类和类加载器的关系,有助于我们解决类冲突、类找不到等问题,也是学习Java反射、动态代理等高级特性的基础。

在Java类加载机制中,双亲委派模型可以类比成一个公司的文件审批流程,这样能更通俗易懂地理解。

什么是双亲委派模型

在Java里,类加载器之间存在层次关系,形成了一个类似树形的结构。除了最顶层的启动类加载器 ,其他类加载器都有自己的“父加载器”,比如扩展类加载器的父加载器是启动类加载器,应用程序类加载器的父加载器是扩展类加载器。双亲委派模型规定,当一个类加载器收到类加载请求时,它首先不会自己去尝试加载这个类,而是把请求委派给父加载器去完成,只有当父加载器无法完成加载任务时,子加载器才会尝试自己去加载。

用公司审批流程举例说明

假设你在公司里写了一份重要的报告,需要经过审批才能正式发布。这就类似于类加载器收到加载类的请求。

  • 部门经理(应用程序类加载器):你作为普通员工,写好报告后会先交给部门经理,对应应用程序类加载器收到类加载请求,它不会自己先去处理,而是把请求向上提交。
  • 部门总监(扩展类加载器):部门经理拿到你的报告后,不会直接审批,而是交给部门总监,这就好比应用程序类加载器把加载请求交给扩展类加载器。部门总监拿到报告后,同样也不会直接审批,而是继续往上提交。
  • 公司老板(启动类加载器):部门总监把报告交给公司老板,启动类加载器作为最顶层,先检查这个报告(类)是不是属于它负责的核心内容,比如公司的基本规章制度、财务审批流程等相关文件(Java核心类库) 。如果是,老板就直接审批(加载类);如果不是,比如是关于某个项目的具体报告,老板就会把报告打回给部门总监,说“这事儿不归我管,你处理一下”。
  • 部门总监处理:部门总监接到老板打回的报告后,查看是否属于自己职责范围内,比如一些技术规范、行业标准相关的报告,如果是就审批(加载类);如果不是,再打回给部门经理。
  • 部门经理处理:部门经理接到报告,一看是关于自己部门项目执行情况的报告,就自己审批了(应用程序类加载器自己加载类)。

双亲委派模型的好处

  • 避免重复加载:如果父加载器已经加载过这个类,子加载器就不需要再加载了,节省了资源。就像在公司审批流程中,如果高层已经审批过类似的文件,底层就不用重复审批。
  • 保证安全性:它确保了Java核心类库的一致性和安全性。因为核心类库都是由最顶层的启动类加载器加载的,防止了用户自定义的类去替换核心类。比如不会出现有人写一个假的java.lang.String类混入系统,因为启动类加载器只会加载真正的核心String类 。

对象的创建过程

咱们把Java中对象的创建过程,比作"盖房子"的过程,这样就很好理解了:

  1. 确定图纸(类加载检查)


1.当你说new 房子()时,JVM首先会检查"房子"这个类的图纸(.class文件)有没有被加载到内存里。如果没加载,就会让类加载器去把图纸搬过来(类加载过程)。
就像盖房子前,必须先有建筑图纸,而且图纸得先拿到工地才行。

  1. 圈地(分配内存)
    图纸确认后,JVM会在内存里找一块合适的地方(堆内存),专门用来盖这个房子(存放对象)。
    相当于开发商在空地上圈出一块地,大小刚好够盖这栋房子。

  1. 地基处理(初始化零值)
    圈好地后,JVM会先把这块地"打扫干净",给里面的各种属性(比如房子的面积、房间数)设置默认值(数字0、布尔false、引用null等)。
    就像盖房子前,先把地皮整平,打好地基,确保基础是合格的。

  1. 挂门牌(设置对象头)
    JVM会给这个对象加一个"身份证"(对象头),里面记录着:这个对象属于哪个类(对应哪个图纸)、哈希码、GC信息等。
    相当于给房子挂上门牌,写明"这是XX小区3号楼",方便后续查找和管理。

  1. 内部装修(执行初始化)
    最后一步是真正按照图纸装修:给属性赋值(比如面积120平米、3个房间)、执行构造方法里的逻辑(比如安装门窗、铺地板)。
    到这一步,房子才算真正盖好,能住人(使用对象)了。

总结一下:从确认图纸→分配空间→基础处理→标记身份→详细装修,一步不差,一个对象就创建出来了。就像盖房子一样,按流程来才能保证最终的"房子"能用、好用。

http://www.xdnf.cn/news/17989.html

相关文章:

  • 8.16打卡 DAY43 复习日
  • 【遥感图像技术系列】遥感图像风格迁移的研究进展一览
  • Mybatis-3自己实现MyBatis底层机制
  • 【leetcode】13. 罗马数字转整数
  • python学习DAY43打卡
  • MySQL 的 DDL / DML / DQL / DCL 做一次系统梳理:概念区别→常用语法→特点与注意点→实战小例子→常见面试/坑点速记
  • redis的key过期删除策略和内存淘汰机制
  • JavaWeb开发_Day14
  • Python虚拟环境与包管理工具(uv、Conda)
  • 发文暴论!线性注意力is all you need!
  • 【LeetCode每日一题】
  • Python---异常链(Exception Chaining)
  • sharding-jdbc读写分离配置
  • Spring——关于Bean以及自动配置
  • FTP上传文件错误
  • BM25算法和传统的TF-IDF算法的区别
  • IEEEtaes.cls解析
  • Trae中`settings.json`文件的Java配置项功能详解(二)
  • 343整数拆分
  • 双椒派E2000D开发板LED驱动开发实战指南
  • 随机整数列表处理:偶数索引降序排序
  • 杂记 03
  • 软件需求工程详解
  • 【自用】JavaSE--特殊文件Properties与XML、日志技术
  • 项目管理进阶——解读大型IT系统集成项目实施要点培训【附全文阅读】
  • 主从复制+哨兵
  • GPFS集群性能压测
  • MySQL的下载安装(MSI和ZIP版本都有)
  • Linux上配置环境变量
  • UDP/TCP套接字编程简单实战指南