类加载器ClassLoader

1.双亲委派模型

java是根据双亲委派模型的加载类的,当一个类加载器加载类时,会先尝试委托给父类加载器去加载,直到到达启动类加载器顶层若加载不了,则再让子类加载器去加载直到类成功加载,否则抛出异常。

双亲委派模型的好处是可以保证类加载的安全性,无论加载哪个类都会向上委托给BootstrapClassLoader类加载器,BootstrapClassLoader加载不了再尝试自己加载,这样就可以保证用不同的类加载器加载可以得到同一个class对象

2. 双亲委派模型的缺陷

依赖双亲委派模型,子类加载器可以使用父类加载器加载的类,而父类加载器无法使用子类加载器加载的类,这是因为子类加载器可以向上委托让父类加载器加载器,而父类加载器无法向下让子类加载器去加载类。这就导致了双亲委派模型并不是能处理所有类加载的场景,它有自己的局限。

另外类加载器有命名空间的概念,一个类加载器的命名空间由这个类加载器加载的类和其父类加载器加载的类组成,并且同一命名不存在全限定名相同的两个类对象,同一命名空间的类是相互可见的,不同命名空间的类不能相互访问。

接下来针对 子类加载器可以使用父类加载器加载的类,而父类加载器无法使用子类加载器加载的类这句话写个简单的例子来证明

3. 用例

首先我们自定义一个类加载器MyClassLoader,它可以加载path路径下的class文件。为了不破坏双亲委派模型,MyClassLoader类没有重写loadClass方法只重写了findClass方法,当父类加载器无法加载指定类的时候,MyClassLoader会调用findClass方法去尝试加载类

以下的类都定义在package包loader下面

public class MyClassLoader extends ClassLoader {

  private String path;

  MyClassLoader() {
    this.path = Objects.requireNonNull(MyClassLoader.class.getResource("/")).getPath();
  }

  MyClassLoader(String path) {
    this.path = path;
  }

  @Override
  public Class<?> findClass(String name) {
    try {
      byte[] bytes = readBytes(name);
      return defineClass(name, bytes, 0, bytes.length);
    } catch (Exception e) {
      return null;
    }
  }

  private byte[] readBytes(String name) throws IOException {
    String realPath = path + "/" + name.replace(".", "/") + ".class";
    System.out.println("&#x81EA;&#x5B9A;&#x4E49;&#x52A0;&#x8F7D;&#x5668;&#x52A0;&#x8F7D;&#x7C7B;" + name + ", class&#x6587;&#x4EF6;&#x8DEF;&#x5F84;:" + realPath);

    try (FileInputStream fileInputStream = new FileInputStream(realPath);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {

      byte[] bytes = new byte[1024];
      while (true) {
        int len = fileInputStream.read(bytes);
        if (len <= 0 0) { break; } bytearrayoutputstream.write(bytes, , len); return bytearrayoutputstream.tobytearray(); }< code></=>

测试代码,下面各个场景都是一样的,定义了myClassLoader类加载器,可以加载路径/Users/xudong下的class文件。因为MyClassLoader由AppClassLoader加载所有它的父类加载器是AppClassLoader

public class ClassC {

  public static void main(String[] args) throws Exception {
    MyClassLoader myClassLoader = new MyClassLoader("/Users/xudong");

    // &#x8BA9;&#x7C7B;&#x521D;&#x59CB;&#x5316;
    Class<?> classA = Class.forName("loader.ClassA", true, myClassLoader);
    Class<?> classB = Class.forName("loader.ClassB", true, myClassLoader);

    System.out.println("classA's classLoader:" + classA.getClassLoader());
    System.out.println("classB's classLoader:" + classB.getClassLoader());
  }
}

场景一

定义两个类ClassA和ClassB,之后将ClassA.class和ClassB.class复制到/Users/xudong/loader目录下

public class ClassA {

}
public class ClassB {

}

类加载器ClassLoader

类加载器ClassLoader

运行测试代码可以看到ClassA和ClassB都由AppClassLoader加载,虽然一开始由myClassLoader加载但根据双亲委派模型会委托给父类加载器加载,父类加载器加载不了再由子类加载。项目classpath和/Users/xudong下都有class文件但AppClassLoader是myClassLoader的父类加载器,因此先由AppClassLoader加载成功返回。

类加载器ClassLoader

场景二

在场景一的基础上,删除classpath路径下的ClassB.class文件,这样的话只有/Users/xudong下有ClassB.class文件了,接着运行测试代码可以看到ClassB类由myClassLoader加载,因为AppClassLoader加载器在classpath下找不到ClassB.class文件,所以由myClassLoader尝试下加载

类加载器ClassLoader

类加载器ClassLoader

场景三

修改ClassB.java重新编译,接着将classpath下的ClassA.class和ClassB.class复制到/Users/xudong/loader路径下,删除classpath下的ClassB.class

public class ClassB {

  static {
    ClassA classA = new ClassA();
    System.out.println("ClassB &#x9759;&#x6001;&#x4EE3;&#x7801;&#x5757;&#x6267;&#x884C;:" + classA.getClass().getClassLoader());
  }
}

运行测试代码可以看到ClassB由MyClassLoader类加载器加载符合场景二的结果,在类加载的时候进行类初始化执行静态代码块,实例化ClassA对象(肯定需要先加载ClassA类),因为ClassB是MyClassLoader加载的所以ClassA也会用MyClassLoader加载,向上委托,因为classpath存在ClassA.class所以会由AppClassLoader加载器加载,这也说明了 子类加载器可以使用父类加载器加载的类。

类加载器ClassLoader

若把classpath下的ClassA.class也删除呢,那么程序不会报错,只是ClassA类由MyClassLoader加载,因为已经加载了ClassA,所以在ClassB实例化的时候不会再加载ClassA:

类加载器ClassLoader

场景四

修改ClassA.java和ClassB.java文件,重新编译,将ClassA.class和ClassB.class复制到/Users/xudong/loader路径下并将classpath下的ClassB.class文件删除

public class ClassA {

  static {
    ClassB classB = new ClassB();
    System.out.println("ClassA &#x9759;&#x6001;&#x4EE3;&#x7801;&#x5757;&#x6267;&#x884C;:" + classB.getClass().getClassLoader());
  }
}

public class ClassB {

}

执行测试代码报错了找不到ClassB,根据前面的场景我们知道ClassA由AppClassLoader加载,接着运行static静态代码块,由AppClassLoader加载ClassB,但classpath下并没有ClassB.class所以加载失败,在这种情况下需要AppClassLoader的子类MyClassLoader才能加载ClassB,但由于双亲委派模型我们知道不知道向下委托加载,这也说明了 父类加载器并不能使用子类加载器加载的类。ClassB不在AppClassLoader类加载器的命名空间里

类加载器ClassLoader

最后如有写错的地方,欢迎指正~

Original: https://www.cnblogs.com/monianxd/p/16627917.html
Author: 默念x
Title: 类加载器ClassLoader

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/622234/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • MySQL中实现中文转拼音

    — 插入数据 INSERT INTO t_base_pinyin ( pin_yin_, code_ ) VALUES ( "a", 20319 ),( &q…

    数据库 2023年6月14日
    099
  • 03-MySQL事务

    数据库事务 1、事务特性 1.1、原子性 即不可分割性,事务要么全部被执行,要么就全部不被执行 1.2、一致性 事务的执行使得数据库从一种正确状态转换成另一种正确状态 1.3、隔离…

    数据库 2023年6月16日
    0102
  • 达梦产品技术支持培训-day8-DM8数据库备份与还原-实操

    Disql 工具:联机数据备份与还原,包括库备份、表空间备份与还原、表备份与还原; DMRMAN 工具:脱机数据库备份还原与恢复; 客户端工具 MANAGER和CONSOLE:对应…

    数据库 2023年6月11日
    077
  • 手把手教你分析MySQL查询性能瓶颈,包教包会

    当一条SQL执行较慢,需要分析性能瓶颈,到底慢在哪? 我们一般会使用 Explain查看其执行计划,从执行计划中得知这条SQL有没有使用索引?使用了哪个索引? 但执行计划显示内容不…

    数据库 2023年5月24日
    076
  • leetcode 148. Sort List 排序链表(中等)

    给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 示例 1: 输入:head = [4,2,1,3]输出:[1,2,3,4] 示例 2: 输入:head …

    数据库 2023年6月16日
    084
  • 多商户商城系统功能拆解21讲-平台端分销订单

    多商户商城系统,也称为B2B2C(BBC)平台电商模式多商家商城系统。可以快速帮助企业搭建类似拼多多/京东/天猫/淘宝的综合商城。 多商户商城系统支持商家入驻加盟,同时满足平台自营…

    数据库 2023年6月14日
    0106
  • 项目管理构建工具——Maven(基础篇)

    项目管理构建工具——Maven(基础篇) 在前面的内容中我们学习了JDBC并且接触到了jar包概念 在后面我们的实际开发中会接触到很多jar包,jar包的导入需要到互联网上进行就会…

    数据库 2023年6月14日
    081
  • Nginx平滑升级版本

    Nginx平滑升级版本 一, 查看现目前版本,准备预升级版本的安装包 #查看nginx版本 /usr/local/nginx/sbin/nginx -v #测试nginx访问是否正…

    数据库 2023年6月11日
    081
  • 详解 Serverless 架构的 6 大应用场景

    Serverless 架构将成为未来云计算领域重要的技术架构,将会被更多的业务所采纳。进一步深究,Serverless 架构在什么场景下有优秀的表现,在什么场景下可能表现得并不是很…

    数据库 2023年6月14日
    082
  • MySQL变量、流程控制和游标

    变量、流程控制和游标 变量 在MySQL数据库的存储过程和函数中,可以使用变量来存储查询或计算的中间结果数据,或者输出最终的结果的数据 系统变量 变量由系统定义,属于服务器级别 […

    数据库 2023年5月24日
    069
  • 【Kubernetes系列】Kubernetes组件介绍

    文章目录 概述 Control Plane(控制面) * etcd(分布式的键值对数据存储系统) kube-apiserver(API服务器) kube-scheduler(调度器…

    数据库 2023年6月6日
    0108
  • 手把手教你写一个SpringMVC框架

    一、介绍 在日常的 web 开发中,熟悉 java 的同学一定知道,Spring MVC 可以说是目前最流行的框架,之所以如此的流行,原因很简单: 编程简洁、上手简单! 我记得刚开…

    数据库 2023年6月14日
    087
  • zabbix

    1. zabbix介绍 2. zabbix特点 3. zabbix配置文件 4. 部署zabbix zabbix介绍 zabbix是一个基于WEB界面的提供分布式系统监视以及网络监…

    数据库 2023年6月14日
    082
  • SMBMS(超市订单管理系统)项目从零开始搭建

    如果需要完整的系统可以加我qq:1842329236 一、搭建一个maven web项目 新建一个maven,并且使用模板 maven的详细创建,及配置请看这篇文章https://…

    数据库 2023年6月16日
    079
  • Linux系统安装JDK

    准备工作 1.去JDK的官网下载一个1.8的安装包 2.解压到linux系统 tar -zxvf jdk-8u311-linux-x64.tar.gz -C /download/c…

    数据库 2023年6月6日
    086
  • MySQL索引:B+树索引

    MySQL索引:B+树索引 B+树索引是传统意义上的索引,这是目前关系型数据库系统中查找最为常用和最为有效的索引。B+树索引的构造类似于二叉树,根据键值快速找到数据 B树 B+树是…

    数据库 2023年5月24日
    085
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球