JVM——自定义类加载器

article/2025/9/18 7:19:04

0. 为什么需要自定义类加载器  

网上的大部分自定义类加载器文章,几乎都是贴一段实现代码,然后分析一两句自定义ClassLoader的原理。但是我觉得首先得把为什么需要自定义加载器这个问题搞清楚,因为如果不明白它的作用的情况下,还要去学习它显然是很让人困惑的。

首先介绍自定义类的应用场景:

1加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自定义ClassLoader在加载类的时候先解密类,然后再加载

2从非标准的来源加载代码如果你的字节码是放在数据库、甚至是在云端,就可以自定义类加载器,从指定的来源加载类

3以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理。这个时候你就需要自定义类加载器从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出在Java虚拟机中运行的类。

1. 双亲委派模型

在实现自己的ClassLoader之前,我们先了解一下系统是如何加载类的,那么就不得不介绍双亲委派模型的实现过程。

//双亲委派模型的工作过程源码
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{// First, check if the class has already been loadedClass 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//父类加载器无法完成类加载请求}if (c == null) {// If still not found, then invoke findClass in order to find the class//子加载器进行类加载 c = findClass(name);}}if (resolve) {//判断是否需要链接过程,参数传入resolveClass(c);}return c;
}

双亲委派模型的工作过程如下:

(1当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。

(2)如果没有找到,就去委托父类加载器去加载(如代码c = parent.loadClass(name, false)所示)。父类加载器也会采用同样的策略查看自己已经加载过的类中是否包含这个类,有就返回,没有就委托父类的父类去加载,一直到启动类加载器。因为如果父加载器为空了,就代表使用启动类加载器作为父加载器去加载

(3如果启动类加载器加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),则会抛出一个异常ClassNotFoundException,然后再调用当前加载器findClass()方法进行加载。 

双亲委派模型的好处:

1)主要是为了安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 String

2)同时也避免了类的重复加载,因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不同的 ClassLoader加载就是不同的两个类。

2. 自定义类加载器

(1)从上面源码看出,调用loadClass时会先根据委派模型在父加载器中加载,如果加载失败,则会调用当前加载器findClass来完成加载。

2)因此我们自定义的类加载器只需要继承ClassLoader,并覆盖findClass方法下面是一个实际例子,在该例中我们用自定义的类加载器去加载我们事先准备好的class文件。

2.1 自定义一个People.java类做例子

public class People {
//该类写在记事本里,在用javac命令行编译成class文件,放在d盘根目录下private String name;public People() {}public People(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String toString() {return "I am a people, my name is " + name;}}

2.2 自定义类加载器

自定义一个类加载器,需要继承ClassLoader类,并实现findClass方法。其中defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class(只要二进制字节流的内容符合Class文件规范)。

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;public class MyClassLoader extends ClassLoader
{public MyClassLoader(){}public MyClassLoader(ClassLoader parent){super(parent);}protected Class<?> findClass(String name) throws ClassNotFoundException{File file = new File("D:/People.class");try{byte[] bytes = getClassBytes(file);//defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.ClassClass<?> c = this.defineClass(name, bytes, 0, bytes.length);return c;} catch (Exception e){e.printStackTrace();}return super.findClass(name);}private byte[] getClassBytes(File file) throws Exception{// 这里要读入.class的字节,因此要使用字节流FileInputStream fis = new FileInputStream(file);FileChannel fc = fis.getChannel();ByteArrayOutputStream baos = new ByteArrayOutputStream();WritableByteChannel wbc = Channels.newChannel(baos);ByteBuffer by = ByteBuffer.allocate(1024);while (true){int i = fc.read(by);if (i == 0 || i == -1)break;by.flip();wbc.write(by);by.clear();}fis.close();return baos.toByteArray();}
}

2.3 在主函数里使用

MyClassLoader mcl = new MyClassLoader(); 
Class<?> clazz = Class.forName("People", true, mcl); 
Object obj = clazz.newInstance();System.out.println(obj);
System.out.println(obj.getClass().getClassLoader());//打印出我们的自定义类加载器

2.4 运行结果


 

至此关于自定义ClassLoader的内容总结完毕。

转载请注明出处:JVM——自定义类加载器_SEU_Calvin的博客-CSDN博客_自定义类加载器


http://chatgpt.dhexx.cn/article/QuHD4dqh.shtml

相关文章

JVM - 类加载器

# 类加载器及类加载器执行过程 JDK版本&#xff1a;1.8 # 1、类加载器子系统 下图为类加载子系统&#xff1a; 类加载子系统负责从文件系统或者网络中加载class文件&#xff0c;class文件在文件开头有特定的文件标识&#xff08;CA FE BA BE&#xff09;。Classloader只负责cl…

java的类加载器以及如何自定义类加载器

ClassLoader作用 类加载流程的"加载"阶段是由类加载器完成的。 类加载器结构 结构&#xff1a;BootstrapClassLoader&#xff08;祖父&#xff09;–>ExtClassLoader&#xff08;爷爷&#xff09;–>AppClassLoader(也称为SystemClassLoader)&#xff08;爸…

类加载器(ClassLoader)

一、类加载器&#xff08;ClassLoader&#xff09; 1.1 什么是类加载器 Java的类加载器是Java虚拟机&#xff08;JVM&#xff09;的重要组成部分&#xff0c;它的主要作用是动态地将Java类加载到JVM中&#xff0c;以便在运行时使用这些类。Java类加载器通常是由JVM的实现者提供…

什么是类加载器,类加载器如何分类

一、类加载器 1.什么是类加载器 类加载器&#xff1a;负责将.class文件&#xff08;存储的物理文件&#xff09;加载到内存中 2.类加载时机&#xff1a; ① 创建类的实例&#xff08;对象&#xff09; ② 调用类的实例方法 ③ 访问类或者接口的类变量&#xff0c;或者为该…

深入理解Java虚拟机——再谈类的加载器——第十二章——中篇

深入理解Java虚拟机——Java虚拟机介绍——第一章 深入理解Java虚拟机——类加载子系统——第二章 深入理解Java虚拟机——运行时数据区和本地方法接口——详细篇——第三章 深入理解Java虚拟机——对象的实例化内存布局与访问定位——超级详细篇——第四章 深入理解Java虚拟机…

类加载器详解

类加载器的分类 JVM支持两种类型的类加载器,分别为引导类加载器(BootstrapClassLoader)和自定义类加载器(User-Defined ClassLoader) 从概念上来讲, 自定义类加载器一般指的是程序中由开发人员自定义的一类,类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类…

Java - 类加载器

文章目录 1. 类加载的过程2. 类加载器的分类2.1 引导类加载器 Bootstrap2.2 扩展类加载器 ExtClassLoader2.3 系统类加载器 AppClassLoader2.4 三者之间的关系2.5 自定义类加载器2.6 注&#xff1a;谁来准备类加载器呢? 3. 双亲委派机制4. ClassLoader抽象类5. URLClassLoader…

DAU 和 MAU

DAU 和 MAU 日活跃用户占月活跃用户的比例越高&#xff0c;表明用户对App的使用粘性越高。 DAU&#xff0c;即&#xff1a;Daily Active User&#xff0c;指日活跃用户数 MAU&#xff0c;即&#xff1a;Monthly Active User&#xff0c;指月活跃用户数。 *例子1&#xff1a;…

如何通过DAU分析活跃用户?(案例:python绘制箱体图)

前言&#xff1a;本文内容以游戏产品为基础进行讲解&#xff0c;内容为以下4部分&#xff1a; 1. 如何理解DAU反映了哪些问题&#xff1f; 2. 有哪些因素会影响DAU变动&#xff1f; 3. 如何解读DAU的“箱体图”&#xff1f; 4. 如何使用python绘制“箱体图”&#xff1f; DAU的…

【数据分析】产品日活DAU下降,怎么分析

目录 案例简介 第一步&#xff1a;确认数据真实性 第二步&#xff1a;明确定义&#xff0c;并拆解指标&#xff0c;进一步定位原异常部分 第三步&#xff1a;根据几个常见维度初步拆分数据 第四步&#xff1a;进一步做假设并细分深入&#xff0c;得出结论 案例分析 例题…

数据分析——DAU下降问题(转)

文章转自&#xff1a; DAU异常下降该如何分析 1. 梳理公司的用户增长模式 尽管不同业务形态、以及不同发展阶段的公司&#xff0c;其用户增长模式各有差异&#xff0c;但都可以从拉新策略和促活策略进行分解。 常见的拉新策略有&#xff1a; 流量采购。比如通过厂商预装、…

有赞一面:亿级用户DAU日活统计,有几种方案?

说在前面 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如极兔、有赞、希音、百度、网易、滴滴的面试资格&#xff0c;遇到一几个很重要的面试题&#xff1a; (1) 亿级用户场景&#xff0c;如何高性能统计日活&#xff1f; (2) 如何实现亿…

数据分析——用户粘性指标 DAU/MAU

&#xff08; 一 &#xff09;定义 DAU&#xff0c;即&#xff1a;Daily Active User&#xff0c;指日活跃用户数&#xff1b; MAU&#xff0c;即&#xff1a;Monthly Active User&#xff0c;指月活跃用户数。 日活/月活就是体现用户粘性最频繁使用的指标。日活跃用户占月活…

数据分析体系 - 用户粘性(DAU/MAU 和 月人均活跃天数)

对于常见的App&#xff0c;用户粘性的取值范围就是3%~100%&#xff0c; 不同领域的App也会有不同的基准值&#xff0c; 例如移动游戏会以20%为基线&#xff0c; 而工具类App会以40%为基线。 用户粘性的两个计算指标&#xff1a; 1、DAU/MAU 2、月平均活跃人数 这里其实…

数据分析——DAU下降/上升原因分析

本文是对“DAU变动原因”问题进行的思考整理&#xff0c;仅作记录&#xff0c;欢迎讨论。 &#xff08; 一 &#xff09;思维框架 图版&#xff1a; 文版&#xff1a; 内部原因&#xff1a; 1. 数据验证 如果DAU上升或者下降&#xff0c;且非日常波动&#xff0c;需先确…

数据分析 — 用户粘性的两个计算指标(DAU/MAU和月人均活跃天数)

很多运营都了解DAU&#xff08;日活跃用户数&#xff09;和MAU&#xff08;月活跃用户数&#xff09;的重要性&#xff0c;但在某些情况下这两个数值本身并不能反映出太多问题&#xff0c;这个时候就要引用到【DAU/MAU】的概念&#xff0c;即【日活/月活】。 用户粘性的两个计…

峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?

小游戏《羊了个羊》 短短的7天内&#xff0c;DAU突破了1亿、吞吐量峰值21WQps。 《羊了个羊》运营后台数据显示&#xff0c;在短短的7天内&#xff0c;这款小游戏的DAU就突破了1亿。 要知道&#xff0c;除了王者荣耀、原神等屈指可数的现象级手游之外&#xff0c;1亿DAU是这个…

dau、mau、pcu、dnu、wau、acu、uv分别是什么意思?

dau、mau、pcu、dnu、wau、acu、uv的意思是什么?怎么分析? DAU(Daily Active User)日活跃用户数量。常用于反映网站、互联网应用或网络游戏的运营情况。 MAU(monthly active users)月活跃用户人数。是在线游戏的一个用户数量统计名词&#xff0c;数量越大意味着玩这款游戏的人…

数据化运营04 DAU、MAU、UV:谁是最有参考价值的活跃指标?

活跃类指标是重要的用户质量指标&#xff0c;代表了产品上真正的用户&#xff0c;代表了具备营销价值的用户群&#xff0c;是几乎所有产品运营的重点。 在这一讲中&#xff0c;我会主要向你介绍 UV、DAU、MAU&#xff0c;以及每日使用时长和每日打开频次等指标。 你是否思考过…

如何理解、分析DNU/DAU?(案例:DNU、DAU面积图)

前言&#xff1a;本文内容以游戏产品为基础进行讲解&#xff0c;内容为以下4部分&#xff1a; 1. 如何理解DNU和DAU 2. 如何使用python绘制DNU和DAU的面积图 3. 如何分析DNU/DAU 4. 如何计算当日DAU有多少由往日N日DAU贡献的&#xff0c;并简单计算用户生命周期&#xff1f; DN…