类加载器和双亲委派模型

news2024/12/25 9:35:43

类加载机制的第一步就是“加载”,即将Class文件获取二进制字节流并加载到方法区中
这个“加载”动作是放在JVM 之外去实现的,能够让应用程序来决定如何获取所需要的类

类和类加载器

对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同决定其在JVM中的唯一性
简答点说:比较两个类是否是同一个类,只有在这两个类是由同一个类加载器加载的前提下才有意义。
否则,即使这两个类来源于同一个Class文件,加载到同一个JVM中,但是它们的类加载器不是同一个,那么就认为这是两个不同的类。
这个”加载“动作是由类加载器完成的,而且”加载“动作放在了JVM之外,所以我们可以自定义类加载器来实现自己的应用程序
可以通过Class.getClassLoader()获取一个类的类加载器

双亲委派模型

站在JVM的角度来看,只存在两种不同的类加载器:

  1. 一种是启动类加载器,Bootstrap ClassLoader,这个类加载底层使用C++来实现,这种类加载器属于JVM自身。
  2. 另一种是其他的所有类加载器,这些类加载器是由Java语言实现,独立于JVM之外,并且全部都继承自抽象类java.lang.ClassLoader

站在Java开发人员的角度来看,类加载器会更加详细。

三层类加载器和双亲委派模型

绝大部分的Java应用程序都会通过以下3个系统提供的类加载器来进行加载

  1. 启动类加载器
    这个类加载器负责加载存放在<JAVA_HOME>\lib目录或被XBootclasspath指定的路径中的类,并且是JVM能够识别的类
    前面说了,这个启动类加载器是JVM自己的,底层是由C++实现的,所以启动类加载器无法在Java应用程序中进行直接引用。

  2. 扩展类加载器
    这个类加载负责加载存放于<JAVA_HOME>\lib\ext目录中或被java.ext.dirs系统变量所指定的路径中的类
    我们可以将自定义的通用的类放在ext目录下来扩展JavaSE的功能
    由于扩展类加载器是由Java实现的,所以我们可以在Java应用程序中直接使用这个扩展类加载器来加载Class文件

  3. 应用程序类加载器
    这个应用程序类加载器ClassLoader类中getSystemClassLoader()的返回值
    负责加载用户类路径classpath上的所有类,也被称为“系统类加载器”
    如果应用程序中没有自定义类加载器,那么一般情况下这个应用程序类加载器就是程序中默认的类加载器**

JDK9之前的Java应用程序都是由这三种类加载器互相配合来完成的,我们还可以通过自定义类加载器来进行拓展
这些类加载器之间的协作关系是

这个图展示的各种类加载器之间的层次关系就被称为类加载器的“双亲委派模型”
双亲委派模型要求:除了顶层的启动类加载器之外,其余的所有类加载器都要有自己的父类类加载器
这里的类加载器之间的父子关系不是通过继承来实现的,通常是使用组合来复用父类类加载器的代码
双亲委派模型的的工作流程:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求 委派给 父类加载器去完成,每一个层次的类加载器都是这样
因此,所有的类加载请求最终都应该传送到最顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时(他的搜索范围中没有找到所需要的这个类),子加载器才会尝试自己去完成加载

这样做的好处是:Java中的类随着它的类加载一起具备了带有优先级的层次关系
简单点说,因为类加载器有优先级,所以由不同类加载器加载的类也就具有了优先级。
例如:java.lang.Object,他存放与rt.jar中,无论是哪一个类加载器要加载这个类,最终都会委派给最顶层的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都能够保证是同一个类。反之,如果没有双亲委派模型,由各自的类加载器来自行去加载的话,如果用户自定义了一个名为java.lang.Object类,并放在了类路径classpath下,那么程序中就会出现多个不同的Object类,JDK中的类就乱套了。

双亲委派模型的实现是非常简单的,来看java.lang.ClassLoader中的loadClass()方法

protected Class<?> loadClass(String name, boolean resolve)  
    throws ClassNotFoundException  
{  
    synchronized (getClassLoadingLock(name)) {  
        // First, check if the class has already been loaded  
        // 首先,检查请求的类是否已经加载过了
        Class<?> c = findLoadedClass(name);  
        if (c == null) {  
            try {  
                if (parent != null) {  
                    c = parent.loadClass(name, false);  
                } else {  
                    c = findBootstrapClassOrNull(name);  
                }  
            } catch (ClassNotFoundException e) {  
                // ClassNotFoundException thrown if class not found  
                // from the non-null parent class loader            
            // 如果父类加载器抛出ClassNotFoundException
            // 说明父类加载无法完成加载请求
            }  
			  
            if (c == null) {  
                // If still not found, then invoke findClass in order  
                // to find the class.                
				// 在父类的加载器无法加载时,
				// 再调用本身的findClass()方法进行加载                
                c = findClass(name);  
  
                // this is the defining class loader; record the stats  
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);  
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);  
                sun.misc.PerfCounter.getFindClasses().increment();  
            }  
        }  
        if (resolve) {  
            resolveClass(c);  
        }  
        return c;  
    }  
}

自定义类加载器

Java提供了一个抽象类加载器ClassLoader,所有的用户自定义类加载器都应该继承自这个抽象类
在代码中最常见的一个类加载器就是ClassLoader,这个抽象类类加载器就是属于上述的三层中的“应用程序加载器”或“系统加载器”
在自定义ClassLoader的子类时,两种做法

  1. 重写loadClass()方法
  2. 重写findClass()方法

建议:重写findClass(),因为在ClassLoader中的loadClass()中会调用findClass(),最好不要修改loadClass()的内部逻辑。
在双亲委派模型中,会先交给父类加载器去加载,如果父类加载器加载失败,则会调用findClass(),因此我们一般实现findClass()来实现子类的类加载逻辑,不去破坏父类的loadClass()方法
来看我们自定义的类加载器,实现加载磁盘上任意位置的Class文件

package com.liumingkai.domain;  
  
import java.io.BufferedInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.FileInputStream;  
import java.io.IOException;  
  
/**  
 * @author 刘明凯  
 * @version 0.0.1  
 * @date 2023年5月11日 13:29  
 */  
public class MyClassLoader extends ClassLoader {  
    private String path;  
  
    public MyClassLoader(String path) {  
        this.path = path;  
    }  
  
    public MyClassLoader(ClassLoader parent, String path) {  
        super(parent);  
        this.path = path;  
    }  
  
    @Override  
    protected Class<?> findClass(String name) throws ClassNotFoundException {  
        BufferedInputStream bis = null;  
        ByteArrayOutputStream baos = null;  
        try {  
            bis = new BufferedInputStream(new FileInputStream(path + name + ".class"));  
            baos = new ByteArrayOutputStream();  
            int len;  
            byte[] data = new byte[1024];  
            while ((len = bis.read(data)) != -1) {  
                baos.write(data, 0, len);  
            }  
            //获取内存中的完整的字节数组的数据  
            byte[] classByteArray = baos.toByteArray();  
            //将字节数组转换为Class的实例  
            return defineClass(null, classByteArray, 0, classByteArray.length);  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                if (null != baos) {  
                    baos.close();  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
            try {  
                if (null != bis) {  
                    bis.close();  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
        return null;  
    }  
}

当定义完成了类加载器后,我们只需要在程序中调用类加载器的loadClass()来加载类即可

public static void main(String[] args) throws Exception {  
    MyClassLoader myClassLoader = new MyClassLoader("D://");  
    Class<?> helloClass = myClassLoader.loadClass("Hello");  
    System.out.println("类加载器是" + helloClass.getClassLoader());  
    Object obj = helloClass.newInstance();  
    System.out.println(obj);  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/514452.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数字藏品的价值和意义

2022年以来&#xff0c;数字藏品概念在国内火热起来。从年初的《关于防范 NFT相关金融风险的倡议》到8月份央行数字货币 DCEP的正式面世&#xff0c;从中国香港首个“NFT”艺术品在香港拍卖市场成交到国内多家互联网大厂推出数字藏品平台&#xff0c;越来越多的企业开始试水数字…

Spring Cloud Alibaba--Nacos服务注册和配置中心

文章目录 一、什么是Nacos1.1、Nacos的由来1.2、Nacos的特性1.3、Nacos的下载和启动 二、Nacos服务注册2.1、代码示例2.2、各种注册中心的比较CAP定理多个注册中心比较 三、Nacos配置中心3.1、Nacos配置管理3.2、代码示例3.3、多环境多项目管理3.3.1、命名空间3.3.2、Group分组…

递归到动态规划:空间压缩技巧-纸币问题的有限张数

这个题是我们纸币问题的第三题 题目大意&#xff1a; arr是货币数组&#xff0c;其中的值都是正数。再给定一个正数aim。 每个值都认为是一张货币&#xff0c; 认为值相同的货币没有任何不同&#xff0c; 返回组成aim的方法数 例如&#xff1a;arr {1,2,1,1,2,1,2}&#xff0…

【C】模拟实现atoi,atof函数

目录 atoi函数 atof函数 模拟实现atoi&#xff0c;atof函数 1、atoi模拟实现 2、atof模拟实现 3、测试案例代码 atoi函数 atoi函数是将字符串转换成整数 函数头文件&#xff1a;#include <stdlib.h> 函数原型&#xff1a;int atoi(const char *str); 参数&…

利用结构相似性做单细胞多模态分析

多模态单细胞测序技术从多层基因组数据中提供了丰富的细胞异质性信息。然而&#xff0c;在没有正确消除模态偏差的情况下去分析联合空间&#xff0c;往往会得到比单模态分析更差的聚类结果。如何有效利用多组学额外信息来描绘细胞状态并识别有意义的信号仍然是一个重大的挑战。…

华为 VOS 移植到 TDA4VM/VH 芯片的 TI RTOS SDK 时的 bug 修复笔记

请从官网下载 TD4VM 技术参考手册&#xff0c;地址如下&#xff1a; TDA4VM 技术参考手册地址 VOS 作为静态库移植到TDA4VM/VH 芯片的 TI RTOS SDK 中 VOS 移植到 mcusw/mcal_drv/mcal/vos&#xff0c;如下&#xff1a; vos 测试应用 在 mcusw/mcuss_demos/vos_test_app …

Shell脚本之正则表达式

目录 一、正则表达式的介绍 1&#xff09;正则表达式的组成 2&#xff09;正则表达式和通配符的区别 二、基础正则表达式 1&#xff09;转义字符的运用 将特殊含义的字符转换为普通字符的含义 将普通字符转换为特殊作用的字符 2&#xff09;基础正则表达式实际应用 查…

C++ | 结构体及大小计算

C结构体及大小计算 文章目录 C结构体及大小计算struct 和 class 区别字节对齐默认对齐方式 位域使用#pragma pack(n)结构体中有结构体Reference struct 和 class 区别 结构体&#xff08;struct&#xff09;和类&#xff08;class&#xff09;有点像&#xff0c;均是定义一个数…

Activi7工作流经典实战(附:常用流程流转代码片段)

一、Activiti7介绍 Activiti正是目前使用最为广泛的开源工作流引擎。Activiti的官网地址是 https:// www.activiti.org 历经6.x和5.x两个大的版本。 1. Activiti工作流引擎 他可以将业务系统中复杂的业务流程抽取出来&#xff0c;使用专门的建模语言BPMN2.0进行定义。业务流…

彻底搞清楚Handler,再也不怕面试官

Handler Handler可以说是Android框架里面很精髓的一部分了&#xff0c;面试必问&#xff0c;用的也最多 Handler是什么&#xff1f; 提到Handler大家一定不陌生&#xff0c;我们经常用它来切换线程&#xff0c;或者是说做一些延时任务等等。最常用的地方可能就是在网络请求中…

Flask全栈解决小问题系列(1)搭建一个bootstrap开发框架

时间不多,闲话少说,实践出真知! 1.目的:为实现FlaskBootStrap开发效果,搞个开发测试项目 2.搭建项目 1)建个test-bootstrap项目,项目目录结构如下: 2)appstart.py内容如下: import json from flask import Flask,redirect,render_templateapp Flask("__main__") …

00后太卷了上班还没3年,跳到我们公司起薪18k....

都说00后已经躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。前段时间我们部门就来了个00后&#xff0c;工作都还没三年&#xff0c;跳到我们公司起薪18K&#xff0c;都快接近我了。 后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。最近和他…

Yolov5/Yolov7改进:小目标到大目标一网打尽,轻骨干重Neck的轻量级目标检测器GiraffeDet

1.GiraffeDet介绍 论文:https://arxiv.org/abs/2202.04256 🏆🏆🏆🏆🏆🏆Yolov5/Yolov7魔术师🏆🏆🏆🏆🏆🏆 ✨✨✨魔改网络、复现前沿论文,组合优化创新 🚀🚀🚀小目标、遮挡物、难样本性能提升 🍉🍉🍉定期更新不同数据集涨点情况 本文是…

gitlab上传大文件限制问题解决

gitlab上传大文件限制问题解决 前景提要&#xff1a; 今天收到同事反馈遇到gitlab 上传大文件时候报如下错误 error: RPC failed; result22, HTTP code 413 fatal: The remote end hung up unexpectedly fatal: The remote end hung up unexpectedly从报错来看是因为文件大…

什么样的冷链保温箱,既环保又实用?

冷链物流运输已经应用在了很多行业中&#xff0c;作为冷链物流运输中的重要设备——冷链保温箱&#xff0c;起到了举足轻重的作用。如果选择不当&#xff0c;选到了劣质产品&#xff0c;尤其是化学行业或者食品行业&#xff0c;就有可能造成试剂失效或者是影响粮食食品安全问题…

2023英码科技激发团队活力,提升集体凝聚力团建拓展之旅圆满结束!

5月6日&#xff0c;时至立夏&#xff0c;风暖昼长&#xff0c;万物繁茂。 在这个生机盎然、活力四射的时节&#xff0c; 尤其适合出游&#xff0c;开展有益身心健康的活动。 这一天&#xff0c;英码科技全体家人们齐聚广州白云区钟落潭&#xff0c;开展一天好玩有趣又意义深…

SVN基本操作 使用教程

01-SVN概述 1、为什么需要SVN版本控制软件 2、解决之道 SCM&#xff1a;软件配置管理 所谓的软件配置管理实际就是对软件源代码进行控制与管理 CVS&#xff1a;元老级产品 VSS&#xff1a;入门级产品 ClearCase&#xff1a;IBM公司提供技术支持&#xff0c;中坚级产品 SVN&…

C++类与对象(三)

文章目录 一.初始化列表1.初始化列表的概念2.初始化列表的注意事项 二.explicit关键字1.单参数构造函数2.多参数构造函数 三.static成员1.static成员的概念2.static成员的特性 四.友元1.概念2.友元函数3.友元类 五.内部类1.概念2.内部类的性质 六.匿名对象七.拷贝对象时编译器的…

Docker安装MySQL主从配置

今天学习Docker安装MySQL主从配置 一、Master 1.1、拉取镜像 $docker pull mysql:8.0.25 1. 2、新建MySQL主服务器的容器实例&#xff0c;端口为3306 docker run -p 3306:3306 --name mysql-master \ -v /data/mysql/mysql-master/log:/var/log/mysql \ -v /data/mysql/mys…

WebSocket聊天功能小Demo

一、WebSocket简介 1.1 什么是WebSocket&#xff1f; WebSocket协议是基于TCP的一种网络协议&#xff0c;它实现了浏览器与服务器全双工&#xff08;Full-duplex&#xff09;通信。它允许服务端主动向客户端推送数据&#xff0c;这使得客户端和服务器之间的数据交换变得更加简…