JVM 学习三:类加载器

类加载器

1 类加载器的分类

JVM 支持两种类型的类加载器:引导类加载器(Bootstrap ClassLoader)自定义类加载器(User-Defined ClassLoader)

从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类加载器,但是 Java 虚拟机规范却没有这么定义,而是将所有派生于抽象类 ClassLoader 的类加载器都划分为自定义类加载器

无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有 3 个,如下所示:
常见的3个类加载器
关于 ClassLoader:
关于 ClassLoader
测试代码:ClassLoaderTest.java

public class ClassLoaderTest {public static void main(String[] args) {// 获取系统(应用)类加载器ClassLoader systemLoader = ClassLoader.getSystemClassLoader();System.out.println(systemLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2// 获取 systemLoader 的上层:扩展类加载器ClassLoader extClassLoader = systemLoader.getParent();System.out.println(extClassLoader); // sun.misc.Launcher$ExtClassLoader@4554617c// 试图获取 extClassLoader 的上层 Bootstrap ClassLoder:获取不到引导类加载器ClassLoader bootstrapLoader = extClassLoader.getParent();System.out.println(bootstrapLoader); // null// 用户自定义类的类加载器:默认使用系统类加载器进行加载ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();System.out.println(classLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2// String 的类加载器也是引导类加载器 --> Java 的核心类库都是使用引导类加载器进行加载的ClassLoader classLoader2 = String.class.getClassLoader();System.out.println(classLoader2); // null}
}

2 虚拟机自带的加载器

2.1 启动类加载器(引导类加载器,Bootstrap ClassLoader)

  • 这个类加载器使用 C/C++ 语言实现的,嵌套在 JVM 内部
  • 它用来加载 Java 的核心库(JAVA_HOME/jre/lib/rt.jar、resources.jar 或 sun.boot.class.path 路径下的内容),用于提供 JVM 自身需要的类
  • 并不继承自 java.lang.ClassLoader,没有父加载器
  • 加载扩展类应用程序类加载器,并指定为它们的父类加载器
  • 出于安全考虑,Bootstrap ClassLoader 启动类加载器只加载包名为 java、javax、sun 开头的类

测试类 ClassLoaderTest2.java

public class ClassLoaderTest2 {public static void main(String[] args) {System.out.println("======== 启动类加载器 ========");// 获取 BootstrapClassLoader 能够加载的 API 路径URL[] urLs = Launcher.getBootstrapClassPath().getURLs();for (URL urL : urLs) {System.out.println(urL.toExternalForm());}// 验证 jsse.jar 包下 Provider 类的类加载器为 Bootstrap ClassLoaderSystem.out.println(Provider.class.getClassLoader()); // null}
}

Provider 类由引导类加载器加载:
Provider.class 由引导类加载器加载
输出结果:
启动类加载器测试代码输出结果

2.2 扩展类加载器(Extension ClassLoader)

  • Java 语言编写的,由 sun.misc.Launcher$ExtClassLoader 实现
  • 派生于 ClassLoader
  • 父类加载器为启动类加载器
  • 从 java.ext.dirs 系统属性所指定的目录中加载类库,或从 JDK 的安装目录的 jre/lib/ext 子目录(扩展目录)下加载类库。如果用户创建的 JAR 放在此目录下,也会自动由扩展类加载器加载

测试类 ClassLoaderTest3.class

public class ClassLoaderTest3 {public static void main(String[] args) {System.out.println("======= 扩展类加载器 =======");String extDirs = System.getProperty("java.ext.dirs");for (String path : extDirs.split(";")) {System.out.println(path);}// 验证 CurveDB 是由 ExtClassLoader 加载器所加载的System.out.println(CurveDB.class.getClassLoader()); // sun.misc.Launcher$ExtClassLoader@7ea987ac}
}

CurveDB 类是由 ExtClassLoader 加载器所加载的
ExtClassLoader 加载器所加载的类
输出结果:
扩展类加载器测试输出结果

2.3 应用程序类加载器(系统类加载器,AppClassLoader)

  • Java 语言编写,由 sun.misc.Launcher$AppClassLoader 实现
  • 派生于 ClassLoader
  • 父类加载器为扩展类加载器
  • 它负责加载环境变量 classpath 或系统属性 java.class.path 指定路径下的类库
  • 应用程序类加载器程序中默认的类加载器,一般来说,Java 应用的类都是由它来完成加载
  • 通过 ClassLoader#getSystemClassLoader() 方法可以获取到该类加载器

测试类 ClassLoaderTest.java

public class ClassLoaderTest4 {public static void main(String[] args) {// 获取用户自定义类的类加载器System.out.println(ClassLoaderTest4.class.getClassLoader());/*** 输出结果为:sun.misc.Launcher$AppClassLoader@18b4aac2*/}
}

3 用户自定义类加载器

在 Java 的日常应用程序开发中,类的加载几乎都是同上述 3 种类加载器相互配合完成的,但是在必要的时候,我们也可以自定义类加载器,来定制类的加载方式。

3.1 为什么要自定义类加载器

  • 隔离加载类
  • 修改类加载的方式
  • 扩展加载源
  • 防止源码泄漏

3.2 自定义类加载器实现步骤

  1. 开发人员可以通过继承抽象类 java.lang.ClassLoader 类的方式,实现自己的类加载器,以满足一些特殊的需求
  2. 在 JDK 1.2 之前,自定义类加载器时,总会去继承 ClassLoader 类并重写 loadClass() 方法,从而实现自定义的加载器类,但是在 JDK 1.2 之后已不再建议用户覆盖 loadClass() 方法,而是建议把自定义类加载的逻辑写在 findClass() 方法中
  3. 在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承 URLClassLoader类,这样就可以避免自己去编写 findClass() 方法及其获取字节码流的方式,使自定义类加载器编写更加简洁

简单的类加载器实现 CustomClassLoader.java

public class CustomClassLoader extends ClassLoader {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] result = getClassFromCustomPath(name);if (result == null) {throw new FileNotFoundException();} else {return defineClass(name, result, 0, result.length);}} catch (FileNotFoundException e) {e.printStackTrace();}throw new ClassNotFoundException(name);}private byte[] getClassFromCustomPath(String name) {// 从自定义路径中加载指定类:细节略// 如果指定路径的字节码文件进行了加密,则需要在此方法中进行解密操作return null;}public static void main(String[] args) {CustomClassLoader customClassLoader = new CustomClassLoader();try {Class<?> clazz = Class.forName("One", true, customClassLoader);Object obj = clazz.newInstance();System.out.println(obj.getClass().getClassLoader());} catch (Exception e) {e.printStackTrace();}}
}

4 关于 ClassLoader

ClassLoader 类是一个抽象类,其后所有的类加载器都继承自 ClassLoader (不包括启动类加载器)

4.1 方法介绍

方法名称描述
getParent()返回该类加载器的超类加载器
loadClass(String name)加载名称为 name 的类,返回结果为 java.lang.Class 类的实例
findClass(String name)查找名为 name 的类,返回结果为 java.lang.Class 类的实例
findLoadedClass(String name)查找名为 name 的已经被加载过的类,返回结果为 java.lang.Class 的类的实例
defineClass(String name, byte[] b, int off, int len)把字节数组 b 中的内容转换为一个 Java 类,返回结果为 java.lang.Class 类的实例
resolveClass(Class<?> c)连接指定的一个 Java 类

4.2 获取 ClassLoader 的途径

  • 获取当前类的 ClassLoader: clazz.getClassLoader()
  • 获取当前线程上下文的 ClassLoader: Thread.currentThread().getContextClassLoader()
  • 获取系统的 ClassLoader: ClassLoader.getSystemClassLoader()
  • 获取调用都的 ClassLoader: DriverManager.getCallerClassLoader()

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

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

相关文章

JVM 学习四:类加载之双亲委派机制与沙箱安全机制

1 双亲委派机制 Java 虚拟机对 Class 文件的加载采用的是按需加载的方式&#xff0c;也就是说&#xff1a;当需要使用该类时才会将它的 Class 文件加载到内存生成 Class 对象&#xff0c;而且加载某个类的 Class 文件时&#xff0c;Java 虚拟机采用的是双亲委派模式&#xff0c…

Java8新特性:Stream介绍和总结

Java8新特性&#xff1a;Stream介绍和总结 什么是Stream 流&#xff08;Stream&#xff09;是数据渠道&#xff0c;用于操作数据源&#xff08;集合、数组等&#xff09;所生成的元素序列。 集合讲的是数据&#xff0c;流讲的是计算 注意&#xff1a; Stream自己不会存储元素…

URL传Base64 造成报错 Illegal base64 character 20

报错如下&#xff1a; errorInternal Server Error, messageIllegal base64 character 20, tracejava.lang.IllegalArgumentException: Illegal base64 character 20 at java.util.Base64Decoder.decode0(Base64.java:714)atjava.util.Base64Decoder.decode0(Base64.java:714) …

Linux 中使用 sort 指令分组排序详解

Linux 中使用 sort 指令分组排序详解 sort 中进行分组排序主要用到的选项为 -k&#xff0c;此文&#xff0c;我们着重于该选项的使用方式&#xff0c;用到的其它选项不做解释&#xff0c;有兴趣的同学可以查看帮助文档 1. 数据准备 现有数据如下&#xff0c;文件名 sort_so…

Shiro-单点登录原理

单点登录原理 一、单系统登录机制 1、http无状态协议 web应用采用browser/server架构&#xff0c;http作为通信协议。http是无状态协议&#xff0c;浏览器的每一次请求&#xff0c;服务器会独立处理&#xff0c;不与之前或之后的请求产生关联&#xff0c;这个过程用下图说明…

@Autowired作用在普通方法上

Autowired作用在普通方法上 Autowired作用在普通方法上&#xff0c;会在注入的时候调用一次该方法&#xff0c;如果方法中有实体参数&#xff0c;会对方法里面的参数进行装配&#xff0c;并调用一次该方法。这个可以用来在自动注入的时候做一些初始化操作。

@Autowired注解作用在方法上

Autowired注解作用在方法上 Autowired注解作用在方法上 &#xff08;1&#xff09;该方法如果有参数&#xff0c;会使用autowired的方式在容器中查找是否有该参数 &#xff08;2&#xff09;会执行该方法

Spring定时任务

Spring定时任务(一)&#xff1a;SpringTask使用 背景&#xff1a;在日常开发中&#xff0c;经常会用到任务调度这类程序。实现方法常用的有&#xff1a;A. 通过java.util.Timer、TimerTask实现。 B.通过Spring自带的SpringTask。 C. 通过Spring结合Quartz实现。本文我们将讲述…

Spring的@Scheduled注解实现定时任务

Spring的Scheduled注解实现定时任务 【简介篇】 项目经常会用到定时任务&#xff0c;实现定时任务的方式有很多种。在Spring框架中&#xff0c;实现定时任务很简单&#xff0c;常用的实现方式是使用注解Scheduled。 Scheduled 常用来实现简单的定时任务。例如凌晨1点跑批&am…

深入学习二叉树(一) 二叉树基础

深入学习二叉树(一) 二叉树基础 前言 树是数据结构中的重中之重&#xff0c;尤其以各类二叉树为学习的难点。一直以来&#xff0c;对于树的掌握都是模棱两可的状态&#xff0c;现在希望通过写一个关于二叉树的专题系列。在学习与总结的同时更加深入的了解掌握二叉树。本系列文…

深入学习二叉树(二) 线索二叉树

深入学习二叉树(二) 线索二叉树 1 前言 在上一篇简单二叉树的学习中&#xff0c;初步介绍了二叉树的一些基础知识&#xff0c;本篇文章将重点介绍二叉树的一种变形——线索二叉树。 2 线索二叉树 2.1 产生背景 现有一棵结点数目为n的二叉树&#xff0c;采用二叉链表的形式…

深入学习二叉树(三) 霍夫曼树

深入学习二叉树(三) 霍夫曼树 1 前言 霍夫曼树是二叉树的一种特殊形式&#xff0c;又称为最优二叉树&#xff0c;其主要作用在于数据压缩和编码长度的优化。 2 重要概念 2.1 路径和路径长度 在一棵树中&#xff0c;从一个结点往下可以达到的孩子或孙子结点之间的通路&…

深入学习二叉树(四) 二叉排序树

深入学习二叉树(四) 二叉排序树 1 前言 数据结构中&#xff0c;线性表分为无序线性表和有序线性表。 无序线性表的数据是杂乱无序的&#xff0c;所以在插入和删除时&#xff0c;没有什么必须遵守的规则&#xff0c;可以插入在数据尾部或者删除在数据尾部。但是在查找的时候&a…

红黑树 —— 原理和算法详细介绍

红黑树 —— 原理和算法详细介绍 R-B Tree简介 R-B Tree&#xff0c;全称是Red-Black Tree&#xff0c;又称为“红黑树”&#xff0c;它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色&#xff0c;可以是红(Red)或黑(Black)。 红黑树的特性: 每个节点或…

微服务雪崩效应与 Hystrix

文章目录微服务雪崩效应微服务中常见的容错方案常见的服务容错思路Hystrix 简介微服务雪崩效应 微服务系统中, 每一个服务专心于自己的业务逻辑, 并对外提供相应的接口, 看上去似乎耦合度比较低, 但经常会遇见这样一种场景: 可以看到, 当 C 服务挂掉时, B 服务还在不断地调用…

时间复杂度到底怎么算

时间复杂度到底怎么算 算法&#xff08;Algorithm&#xff09;是指用来操作数据、解决程序问题的一组方法。对于同一个问题&#xff0c;使用不同的算法&#xff0c;也许最终得到的结果是一样的&#xff0c;但在过程中消耗的资源和时间却会有很大的区别。 那么我们应该如何去衡…

十分钟搞定时间复杂度(算法的时间复杂度)

十分钟搞定时间复杂度&#xff08;算法的时间复杂度&#xff09; 我们假设计算机运行一行基础代码需要执行一次运算。 int aFunc(void) {printf("Hello, World!\n"); // 需要执行 1 次return 0; // 需要执行 1 次 }那么上面这个方法需要执行 2 次运算 …

java实现简单二叉树

二叉树基本知识&#xff1a; 一、树的定义 树是一种数据结构&#xff0c;它是由n&#xff08;n>1&#xff09;个有限结点组成一个具有层次关系的集合。 树具有的特点有&#xff1a; &#xff08;1&#xff09;每个结点有零个或多个子结点 &#xff08;2&#xff09;没有…

HashMap 学习笔记

1.HashMap 的类继承关系 图示即为 Map 相关类的继承关系。源码中的类签名如下: public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {...... }2.HashMap 的底层存储结构 HashMap 的底层存储结构是 Node 类,…

十大经典排序算法动画与解析(配代码完全版)

排序算法是《数据结构与算法》中最基本的算法之一。 排序算法可以分为内部排序和外部排序。 内部排序是数据记录在内存中进行排序。 而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 常见的内部排序算法有&…