类加载过程和类加载器

类加载的过程

  • 加载->连接(验证->准备->解析)->初始化

    类加载过程

加载

1.获得二进制字节流(可以从本地jar 网络或者动态代理获得)

2.转化成方法区中的运行时数据

3.获得类对应的Class对象

加载的过程由类加载器完成,后面会介绍到。

验证

主要验证二进制字节流的格式符合规范,不会危害计算机安全

准备

为类变量分配空间并使用默认值初始化

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,也就是得到类或者字段、方法在内存中的指针或者偏移量。

初始化

为类变量赋予正确的初始值

类的加载的最终产品是位于内存中的Class对象。Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

类加载器

类加载器的层次

JVM 中内置了三个重要的 ClassLoader

  1. BootstrapClassLoader(启动类加载器):最顶层的加载类,由 C++实现,通常表示为 null,并且没有父级,主要用来加载 JDK 内部的核心类库( %JAVA_HOME%/lib目录下的 rt.jarresources.jarcharsets.jar等 jar 包和类)以及被 -Xbootclasspath参数指定的路径下的所有类。
  2. ExtensionClassLoader(扩展类加载器):主要负责加载 %JRE_HOME%/lib/ext 目录下的 jar 包和类以及被 java.ext.dirs 系统变量所指定的路径下的所有类。
  3. AppClassLoader(应用程序类加载器):面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。

除了 BootstrapClassLoader 是 JVM 自身的一部分之外,其他所有的类加载器都是在 JVM 外部实现的,并且全都继承自 ClassLoader抽象类。

类加载器层次关系图

 双亲委派

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {//首先,检查该类是否已经加载过Class c = findLoadedClass(name);if (c == null) {//如果 c 为 null,则说明该类没有被加载过long t0 = System.nanoTime();try {if (parent != null) {//当父类的加载器不为空,则通过父类的loadClass来加载该类c = parent.loadClass(name, false);} else {//当父类的加载器为空,则调用启动类加载器来加载该类c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {//非空父类的类加载器无法找到相应的类,则抛出异常}if (c == null) {//当父类加载器无法加载时,则调用findClass方法来加载该类//用户可通过覆写该方法,来自定义类加载器long t1 = System.nanoTime();c = findClass(name);//用于统计类加载器相关的信息sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {//对类进行link操作resolveClass(c);}return c;}
}

结合上面的源码,简单总结一下双亲委派模型的执行流程:

  • 在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载(每个父类加载器都会走一遍这个流程)。
  • 类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父加载器 loadClass()方法来加载类)。这样的话,所有的请求最终都会传送到顶层的启动类加载器 BootstrapClassLoader 中。
  • 只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载(调用自己的 findClass() 方法来加载类)。

 自定义类加载器

什么时候会用到

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

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

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

实践

1、如果不想打破双亲委派模型,那么只需要重写findClass方法即可

2、如果想打破双亲委派模型,那么就重写整个loadClass方法

这里不打破双亲委派

public class People {private String name;public People() {}public People(String var1) {this.name = var1;}public String getName() {return this.name;}public void setName(String var1) {this.name = var1;}public String toString() {return "I am a people, my name is " + this.name;}
}
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:\\project\\idea\\Learining-Java\\src\\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();}
}

最后测试:

public class Test {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {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());//打印出我们的自定义类加载器}
}

注意,People.java移动到其他地方或者删除,因为自定义的类加载器也遵循双亲委派,所以在加载的时候AppClassLoader类加载器会加载People.java文件。

 

 

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

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

相关文章

CSS练习

CSS练习 工具代码运行结果 工具 HBuilder X 代码 <!DOCTYPE html> <!-- 做一个表格&#xff0c;6行4列实现隔行换色&#xff08;背景色&#xff09;并且第3列文字红色第一个单元格文字大小30px。最后一个单元格文字加粗--> <html><head><meta ch…

HCIP——堆叠技术

堆叠 一、简介二、堆叠的优势1、提高可靠性2、简化组网3、简化管理4、强大的网络拓展能力 三、堆叠的方式1、堆叠卡堆叠2、业务口堆叠 四、堆叠的原理1、角色2、单机堆叠3、堆叠ID4、堆叠的优先级5、堆叠的建立过程 五、堆叠的配置 一、简介 堆叠技术 — 可以将多台真是得物理…

AI Chat 设计模式:14. 适配器模式

本文是该系列的第十四篇&#xff0c;采用问答式的方式展开&#xff0c;问题由我提出&#xff0c;答案由 Chat AI 作出&#xff0c;灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 关于适配器模式&#xff0c;如果由浅入深的来考察&#xff0c;你会依次提出什么问题…

jupyter切换conda虚拟环境

环境安装 conda install nb_conda 进入你想使用的虚拟环境&#xff1a; conda activate your_env_name 在你想使用的conda虚拟环境中&#xff1a; conda install -y jupyter 在虚拟环境中安装jupyter&#xff1a; conda install -y jupyter 重启jupyter 此时我们已经把该安装…

springboot邮件任务

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency> 依赖 配置文件 spring.mail.username1393087444qq.com spring.mail.password************* spring.mail.hos…

== 和 equals 的对比 [面试题]

和 equals 的对比[面试题] 文章目录 和 equals 的对比[面试题]1. 和 equals 简介2. Object 类中 equals() 源码3. String 类中 equals() 源码4. Integer 类中 equals() 源码5. 如何重写 equals 方法 1. 和 equals 简介 是一个比较运算符 &#xff1a;既可以判断基本数据类型…

谷歌关闭跨域限制.(生成一个开发浏览器),Chrome关闭跨域

(一)、首先找到浏览器在电脑磁盘中的位置,并复制 (二)、复制一个浏览器的快捷方式到桌面(不影响正常浏览器) (三)、chrom鼠标右键属性&#xff0c;修改快捷方式的目标 &#xff08;四&#xff09;chrome.exe 后面添加 --disable-web-security --user-data-dir 复制的Chrome浏览…

Threejs学习01——坐标轴展示立方体并实现来回移动

在三维坐标轴上展示立方体并实现来回移动 这是一个非常简单基础的threejs的学习应用&#xff01;创建应用先创建一个场景Scene&#xff0c;然后创建爱你一个透视相机PerspectiveCamera&#xff0c;然后创建立方体BoxGeometry&#xff0c;立方体添加一些材质&#xff0c;将立方…

UI自动化环境的搭建(python+pycharm+selenium+chrome)

最近在做一些UI自动化的项目&#xff0c;为此从环境搭建来从0到1&#xff0c;希望能够帮助到你&#xff0c;同时也是自我的梳理。将按照如下进行开展&#xff1a; 1、python的下载、安装&#xff0c;python环境变量的配置。 2、pycharm开发工具的下载安装。 3、selenium的安装。…

SpringBoot系列之基于Jersey实现文件上传API

前言 JAX-RS&#xff1a;JAX-RS是可以用可以用于实现RESTFul应用程序的JAVA API&#xff0c;给开发者提供了一系列的RESTFul注解Jersey&#xff1a;是基于JAX-RX API的实现框架&#xff0c;用于实现RESTful Web 服务的开源框架。 JAX-RX常用的注解&#xff1a; javax.ws.rs.Pa…

Nonebot实战之编写插件1

前言 应粉丝群内粉丝要求&#xff0c;我也决定写一个Nonebot插件编写教程&#xff0c;从0开始教学。有些不对的地方也欢迎大家指正&#xff0c;修改。 开始 准备 合适的代码编辑器一定的python基础懂得提问的方式 代码编辑器 代码编辑器有很多种选择&#xff0c;比如 vsc…

阿里云服务器地域怎么选?可以改吗?

阿里云服务器地域和可用区怎么选择&#xff1f;地域是指云服务器所在物理数据中心的位置&#xff0c;地域选择就近选择&#xff0c;访客距离地域所在城市越近网络延迟越低&#xff0c;速度就越快&#xff1b;可用区是指同一个地域下&#xff0c;网络和电力相互独立的区域&#…