Java基础- 自定义类加载器
自定义类加载器
在 Java 中实现自定义类加载器通常涉及继承 ClassLoader 类并重写其 findClass 方法。自定义类加载器允许我们从非标准来源(如网络、加密文件或其他媒体)加载类。下面是实现自定义类加载器的基本步骤:
1. 继承 ClassLoader 类
创建一个新的类并继承 ClassLoader 类。例如:
public class MyClassLoader extends ClassLoader {// 类的实现
}
2. 重写 findClass 方法
在自定义类加载器中重写 findClass 方法。这是类加载的核心,我们需要在这里实现查找类字节码并定义类的逻辑。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {// 实现加载类的逻辑// 比如从文件系统、网络或其他源读取类的字节码
}
3. 读取类数据
在 findClass 方法中,需要实现读取类的字节码数据的逻辑。这可能涉及读取文件、网络资源等。
4. 调用 defineClass 方法
一旦获得了类的字节码,使用 defineClass 方法来将这些字节码转换为 Class 对象。defineClass 是 ClassLoader 类的一个受保护方法,可以将一个字节数组转换为 Class 类的实例。
byte[] bytes = ... // 从文件或其他地方获取的类字节码
Class<?> c = defineClass(name, bytes, 0, bytes.length);
5. 返回类对象
在 findClass 方法的末尾,返回通过 defineClass 方法创建的类对象。
完整示例
以下是一个简单的自定义类加载器示例,它从文件系统加载类:
public class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadClassData(name);return defineClass(name, data, 0, data.length);} catch (IOException e) {throw new ClassNotFoundException("Could not load class " + name, e);}}private byte[] loadClassData(String name) throws IOException {String path = classPath + name.replace('.', '/') + ".class";InputStream is = new FileInputStream(path);ByteArrayOutputStream byteStream = new ByteArrayOutputStream();int nextValue = 0;while ((nextValue = is.read()) != -1) {byteStream.write(nextValue);}return byteStream.toByteArray();}
}
在这个示例中,MyClassLoader 从指定的文件路径读取类文件,将文件内容转换为字节数组,然后使用 defineClass 方法来创建 Class 对象。
注意事项
- 安全考虑:自定义类加载器在处理类字节码时应确保数据的来源是安全的。
- 性能考虑:合理管理资源,如关闭打开的文件流。
- 遵循双亲委派模型:默认情况下,类加载器会先尝试让其父类加载器加载类,以保证 Java 核心库的类不会被覆盖。如果需要打破这一模型,需谨慎操作并充分理解可能的后果。
自定义类加载器使得 Java 程序能够以灵活和动态的方式加载类,但同时需要确保代码的安全和有效性。
findClass方法
在 Java 中,findClass 方法是 ClassLoader 类的一部分,用于加载类的具体实现。这个方法在自定义类加载器中尤其重要,因为它提供了一种机制来查找和加载类,特别是当类不在标准的类加载路径中时。
基本功能
findClass 方法被设计用来在类加载器的父级加载器都未能成功加载类时,查找并加载类。当一个类被加载时,ClassLoader 类的 loadClass 方法会首先调用父类加载器尝试加载该类。如果父类加载器无法加载该类(通常因为类不在它们的搜索路径中),loadClass 方法会调用 findClass 方法来尝试加载该类。
方法签名
findClass 方法通常如下定义:
protected Class<?> findClass(String name) throws ClassNotFoundException
参数说明:
name:需要加载的类的完全限定名。
使用场景
- 自定义类加载器:在创建自定义类加载器时,通常需要重写
findClass方法。这允许开发者定义自己的类查找逻辑,比如从特定的文件路径、数据库或其他来源加载类。 - 插件和模块化系统:在实现插件或模块化系统时,
findClass方法可以被用来从模块化的、分离的资源中加载类。
实现注意事项
- 重写
findClass:在自定义类加载器中,通常需要重写findClass方法以提供新的类查找和加载机制。 - 调用
defineClass:在findClass方法中,一旦类的字节码被找到和加载,通常会调用ClassLoader类的defineClass方法来将字节码转换成Class对象。 - 异常处理:如果
findClass无法找到或加载类,它应该抛出ClassNotFoundException。
示例
下面是一个简单的自定义类加载器示例,展示了如何重写 findClass 方法:
public class MyClassLoader extends ClassLoader {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {byte[] b = loadClassData(name);return defineClass(name, b, 0, b.length);}private byte[] loadClassData(String name) {// 实现从文件系统、网络或其他来源加载类字节码的逻辑// ...}
}
在这个例子中,MyClassLoader 类重写了 findClass 方法,以提供从特定来源加载类的逻辑。一旦类的字节码被加载,它使用 defineClass 方法来创建 Class 对象。
结论
findClass 方法是自定义类加载器实现中的关键部分,它提供了一种灵活的方式来扩展 Java 的类加载机制。通过重写 findClass,开发者可以控制如何查找和加载类,这在实现插件系统、应用服务器或处理非标准类加载请求时尤其有用。
defineClass方法
在 Java 中,defineClass 方法是 ClassLoader 类的一个关键方法,用于将字节数组转换成 Class 对象。这个方法在自定义类加载器中尤其重要,因为它允许开发者从非标准来源加载类,例如从网络、加密文件等。
基本功能
defineClass 方法的主要功能是将一个字节数组(包含类的字节码)转换成一个 Class 对象。这个方法通常在自定义的类加载器中被重写或调用,以实现特定的类加载机制。
方法签名
ClassLoader 类中的 defineClass 方法有几个重载版本。最常用的版本之一的签名如下:
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
参数说明:
name:预期的类名,可以为null。b:包含类定义信息的字节数组。off:类定义的起始偏移量(在字节数组中)。len:类定义的长度。
使用场景
- 自定义类加载器:当开发者需要从非标准源加载类(如网络资源、加密文件)时,会创建自定义类加载器,并在其中使用
defineClass方法来实现类的加载。 - 动态类加载:在一些动态生成类的应用场景中(如某些框架或工具),
defineClass方法用于将运行时生成的字节码转换为Class对象。
安全和限制
- 安全检查:
defineClass方法在将字节数组转换为Class对象时,会执行安全检查,确保类的结构和行为符合 JVM 规范。 - 访问权限:由于这个方法被定义为
protected,因此只能在ClassLoader类或其子类中被调用。 - 类名限制:如果指定了类名(
name参数非null),那么加载的类名必须与此名称匹配。 - 封装性限制:在 Java 9 及以后版本中,模块系统的引入对类加载器的行为做了进一步的限制,特别是在封装性方面。
示例
下面是一个简单的示例,展示如何在自定义类加载器中使用 defineClass:
public class MyClassLoader extends ClassLoader {public Class<?> defineClass(String name, byte[] b) {return defineClass(name, b, 0, b.length);}
}
在这个示例中,MyClassLoader 类扩展了 ClassLoader,并提供了一个方法来定义类。这个方法接受一个类名和一个字节数组,然后调用 defineClass 来创建 Class 对象。
结论
defineClass 方法是 Java 类加载机制的一个重要组成部分,它使得开发者可以在运行时动态地加载和定义类。这个方法的正确使用对于实现灵活且安全的类加载策略至关重要。
总结
在 Java 中,当使用 ClassLoader 类的 loadClass 方法加载一个类时,loadClass 方法内部会按照一定的逻辑来决定如何加载这个类。如果这个类之前没有被加载过,loadClass 方法会最终调用 findClass 方法来加载这个类。这是 ClassLoader 类的内部机制,因此在使用 loadClass 方法时,不需要直接调用 findClass 方法。下面是这个过程的简化描述:
-
调用
loadClass方法:当我们在main函数中调用myClassLoader.loadClass("your_class_name")时,实际上是调用了ClassLoader类的loadClass方法。 -
检查类是否已加载:
loadClass方法首先检查这个类是否已经被加载过。如果已经加载,它会直接返回这个类的Class对象。 -
委托给父类加载器:如果类还没有被加载,
loadClass方法会尝试让父类加载器去加载这个类。如果父类加载器不存在或无法加载该类,loadClass方法会调用findClass方法。 -
调用
findClass方法:findClass方法是一个protected方法,通常在自定义类加载器中被重写。它包含了从特定来源(如文件系统、网络等)加载类的具体逻辑。 -
返回
Class对象:一旦findClass方法成功加载了类,并返回了相应的Class对象,loadClass方法就会将这个Class对象返回给调用者。
一般在自定义类加载器的实现中,MyClassLoader 类会重写 findClass 方法以从特定路径加载类文件。当我们在 main 方法中调用 loadClass 时,如果 your_class_name 类之前未被加载,MyClassLoader 的 loadClass 方法会间接调用我们重写的 findClass 方法来加载这个类。这就是为什么在 main 函数中并没有显式调用 findClass 方法,但该方法仍然被执行的原因。
如下是一个测试代码:
package per.mjn.t3;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;public class Load7 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {// 创建自定义类加载器对象MyClassLoader myClassLoader = new MyClassLoader();Class<?> c1 = myClassLoader.loadClass("Demo1_1");Class<?> c2 = myClassLoader.loadClass("Demo1_1");System.out.println(c1);// 判断两次加载获得的类对象是否相同System.out.println(c1 == c2); // true// 即使多次执行loadClass方法,但实际上类文件只会加载一次,第一次加载后就会放在自定义类加载器的缓存中// 下次再调用loadClass()时就可以在缓存中找到了,不会重复的进行类加载// -------------------------------------------------------MyClassLoader myClassLoader1 = new MyClassLoader();Class<?> c3 = myClassLoader1.loadClass("Demo1_1");// 唯一确定类的方式是,包名 类名相同,而且类加载器也是同一个,才认为这两个类才是完全一致的// 虽然MapImpl1与刚才的包名类名一样,但是由于他俩的类加载器对象不是同一个,所以认为这两个类不是同一个类// 也就是,这个类会被加载两次,因为是不同的类加载器,就认为他俩是相互隔离的,不会产生冲突System.out.println(c1 == c3); // false// 创建这个类的实例对象会触发静态代码块的执行c1.newInstance(); // 会打印出"init..."(在静态代码块中提前写好的)}
}class MyClassLoader extends ClassLoader {@Override // name 就是类名称protected Class<?> findClass(String name) throws ClassNotFoundException {String path = "D:\\ideaProject\\JVM_Detect\\src\\per\\mjn\\t1\\" + name + ".class";ByteArrayOutputStream os = new ByteArrayOutputStream();try {Files.copy(Paths.get(path), os);// 得到字节数组byte[] bytes = os.toByteArray();// byte[] -> *.classClass<?> aClass = defineClass(name, bytes, 0, bytes.length);return aClass;} catch (IOException e) {e.printStackTrace();throw new ClassNotFoundException("类文件未找到", e);}}
}
相关文章:
Java基础- 自定义类加载器
自定义类加载器 在 Java 中实现自定义类加载器通常涉及继承 ClassLoader 类并重写其 findClass 方法。自定义类加载器允许我们从非标准来源(如网络、加密文件或其他媒体)加载类。下面是实现自定义类加载器的基本步骤: 1. 继承 ClassLoader …...
2022年高校大数据挑战赛A题工业机械设备故障预测求解全过程论文及程序
2022年高校大数据挑战赛 A题 工业机械设备故障预测 原题再现: 制造业是国民经济的主体,近十年来,嫦娥探月、祝融探火、北斗组网,一大批重大标志性创新成果引领中国制造业不断攀上新高度。作为制造业的核心,机械设备在…...
洛谷 P1998 阶乘之和 C++代码
前言 今天我们来做洛谷上的一道题目。 网址:[NOIP1998 普及组] 阶乘之和 - 洛谷 西江月夜行黄沙道中 【宋】 辛弃疾 明月别枝惊鹊,清风半夜鸣蝉。稻花香里说丰年,听取WA声一片。 七八个星天外,两三点雨山前。旧时茅店社林边&…...
洛谷 B2006 地球人口承载力估计 C++代码
目录 前言 思路点拨 AC代码 结尾 前言 今天我们来做洛谷上的一道题目。 网址:地球人口承载力估计 - 洛谷 题目: 思路点拨 经典牛吃草问题。 解设一个人一年吃一份草。 则x*a-y*b为会多出的草,为什么会多呢?是因为每年都有…...
少走弯路:OpenCV、insightface 等多方案人脸推理和识别
脑壳有包又花时间折腾了一下,其实之前也折腾过,主要是新看了一个方法 在下图中查找脸部 第一种方案: 使用了opencv 的cv2.FaceDetectorYN. ,完整代码如下: import numpy as np import cv2imgcv2.imread("00000…...
github代码连接vercel 建立一个公用网站
Deploying to the Cloud using Vercel 前置任务 建立一个基于flask的web app代码库并上传至github repo Vercel用途 vercel有点像一个免费的cloud server,帮助你将flask框架下的程序运行在云端。可以public访问。 deploy流程 在主文件夹中建立requirements.tx…...
使用pandas将字符串格式数据转换为单独的行
有时在处理数据时,可能会遇到这样的情况,即数据框中的整个字符串条目需要拆分到不同的行中。这可能是一项具有挑战性的任务,特别是当数据庞大而复杂时。尽管如此,一个名为pandas的Python库提供了各种函数,使用这些函数…...
【Tkinter 入门教程】
【Tkinter 入门教程】 1. Tkinter库的简介:1.1 GUI编程1.2 Tkinter的定位 2. Hello word! 程序起飞2.1 第⼀个程序2.2 字体颜色主题 3. 组件讲解3.1 tkinter 的核⼼组件3.2 组件的使⽤3.3 标签Label3.3.1 标签显示内容3.3.2 多标签的应⽤程序3.3.3 总结 3.4 按钮but…...
深入理解Java中继承的高级使用方案
摘要: 继承是Java中的一项强大的特性,它允许子类从父类中继承属性和方法。然而,继承的高级使用方案涉及更复杂的概念和技术,可以帮助开发人员构建更加灵活、可维护和可扩展的代码。本文将深入探讨Java中继承的高级用法,…...
nexus私服开启HTTPS
maven3.8.1以上不允许使用HTTP服务的仓库地址,如果自己搭建的私服需要升级为HTTPS或做一些设置,如果要升级HTTPS服务有两种方式:1、使用Nginx开启HTTPS并反向代理nexus;2、直接在nexus开启HTTPS。这里介绍第二种方式 1、在ssl目录…...
融合CFPNet的EVC-Block改进YOLO的太阳能电池板缺陷检测系统
1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 随着太阳能电池板的广泛应用,对其质量和性能的要求也越来越高。然而,由于生产过程中的各种因素,太阳能电池板上可能存在各种缺…...
传媒行业CRM:打造高效客户管理,提升品牌影响力
传媒行业充满竞争和变化,传媒企业面临着客户管理不透明、业务流程混乱、销售数据分析不足,无法优化营销策略和运营管理等问题。CRM系统是企业实现数智化管理的神器,可以有效解决这些问题。下面说说,传媒行业CRM系统推荐。 1、建立…...
基于深度学习的肺炎CT图像检测诊断系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习在肺炎CT图像检测诊断方面具有广泛的应用前景。以下是关于肺炎CT图像检测诊断系统的介绍: 任务…...
YOLOv8改进 | 2023 | SCConv空间和通道重构卷积(精细化检测,又轻量又提点)
一、本文介绍 本文给大家带来的改进内容是SCConv,即空间和通道重构卷积,是一种发布于2023.9月份的一个新的改进机制。它的核心创新在于能够同时处理图像的空间(形状、结构)和通道(色彩、深度)信息…...
Python 全栈体系【四阶】(一)
四阶:机器学习 - 深度学习 第一章 numpy 一、numpy 概述 Numerical Python,数值的 Python,补充了 Python 语言所欠缺的数值计算能力。 Numpy 是其它数据分析及机器学习库的底层库。 Numpy 完全标准 C 语言实现,运行效率充分优…...
Git【成神路】
目录 1.为啥要学git啊?😕😕😕 2.版本控制软件的基本功能 🤞🤞🤞 3.集中式版本控制 🤶🤶🤶 4.分布式版本控制😎😎😎 …...
文件操作详解
文件操作详解 一:文件相关概念1:问什么使用文件2:什么是文件???2.1:程序文件2.2数据文件 二:文件的打开和关闭1:流的定义2:标准流3:文件指针 一&a…...
模块 A:web理论测试
模块 A:理论测试 任务一:单选题 1.为 EMP 表的 namesalary 字段创建名为 emp name salary idx 的校复习接课 name 字段升序, salary 字段降序的复合索引的 SQL 语句是? B A: CREATEINDEX emp name salary idx ON EMP(namesalary) B: …...
git rebase冲突说明(base\remote\local概念说明)
主线日志及修改 $ git log master -p commit 31213fad6150b9899c7e6b27b245aaa69d2fdcff (master) Author: Date: Tue Nov 28 10:19:53 2023 08004diff --git a/123.txt b/123.txt index 294d779..a712711 100644 --- a/123.txtb/123.txt-1,3 1,4 123 4^Mcommit a77b518156…...
函数式接口的妙用,让异步执行更简单
你是否曾经遇到过在SpringBoot中Async注解无法正常工作的问题?今天,我们用函数式接口来解决这个问题。 一、什么是函数式接口? 函数式接口(Functional Interface)是 Java 8 中引入的一个概念,是指只包含一…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
