20、java中的类加载机制

1、类加载机制是什么?

       类加载机制指的就是jvm将类的信息动态添加到内存并使用的一种机制。

2、那么类加载的具体流程是什么呢?

        一般说类加载只有三步:加载、连接和初始化,其中连接包括验证、准备和解析,用于将运行时加载的类文件添加到jre环境中使用。

                               

 

加载:加载就是将类文件读取到内存中并对类信息做了初步的处理。这里是有讲究的,jvm一启动就会先加载一些类,这些类位于jdk中的rt.jar中,而另外的类就不会加载,而是当用到时才加载,这也就是预加载和运行时加载,运行时加载时jvm会先去内存中查看是否存在要使用的类,如果没有则按着这个类的全限定名进行加载,之前在IO部分也说过,像这种文件,在内存中是以一种数据序列的形式存在,也就是加载到内存的是类文件的二进制流信息,另外就是这个二进制流可以通过多条路径获取,不一定非要读取磁盘上的文件信息。然后jvm不会闲着,他会将类中的静态信息如:类信息、静态变量、常量等添加到jvm中的方法区,一般还会在堆中创建一个Class的对象用来表示这个类的信息。

验证:对加载到内存的二进制流的信息进行合法性校验,一切为了安全。试想一下,如果叮咣叮咣的写一堆三字经到.class文件,然后加载到内存,这时候jvm一运行不崩溃才怪。

准备:这个阶段主要是对类变量进行操作,如果仅是类变量,那么会根据其类型在方法区给变量开辟空间,并附一个对应类型的默认值。如果变量除了使用static修饰外,还用final修饰,那么则给变量赋定义的值。

解析:其实这一个我也不是太理解,解析是虚拟机将常量池内的符号引用解析为直接引用。以下为自己理解,大白话描述:何为符号引用,我理解的就是使用源码中的类名、方法名、变量名等来应用内存中对应的地址域所对应的数据,而直接引用呢就是将这些名称进行解析成一个可以直接指向内存地址的变量,直接引用的数据一定在内存中已经存在了,而符号引用所对应的数据不一定存在,我自认为的就是只是声明了一个变量,没有赋值,所以有的时候他自己都不知道自己到底指向哪里。

初始化:就是根据类中的构造方法来初始化类的过程,例如:我们都知道静态变量是随着类的加载而加载的,所以这里就会给静态变量初始化一个值,而对于那些成员方法,如果在构造器中没有调用的话,他们是不会被调用的。说的通俗易懂就是初始化那些被static修饰的部分,因为随着类的加载而加载。

3、加载类的流程具体是谁实现的?

       上面一直说jvm加载类,那么如果再往细处划分又是怎么样的呢?其实jvm中有专门管着加载类的工具,这就是类加载器。类加载器主要用于根据类的全限定名将对应的class文件的流信息加载到虚拟机内存,并将其转为Class对象。类加载器有四种:

       启动类加载器(Bootstrap ClassLoader):加载\Java\jdk1.8\jre\lib 下的类

       扩展类加载器(Extension ClassLoader):加载\Java\jdk1.8\jre\lib\ext下的类

       应用程序类加载器(Application ClassLoader):加载用户路径上的类,就是开发者自定义的类

       自定义加载器:加载自定义位置的类文件,为了避免以上三个加载器都加载不到指定的包。

类加载器的主要作用是根据类的全类名将类的字节码信息添加到内存中并根据字节码信息创建一个Class对象来表示这个类信息,这里充分体现了面向对象的思想,万物皆可为对象,每一个不同类的信息的就是类的一个对象。

类加载器的基本用法:

public static void main(String[] args) throws ClassNotFoundException {// 返回系统类加载器,平时使用不多,常用当前类的加载器去获取指定的类信息//ClassLoader classLoader = ClassLoader.getSystemClassLoader();// 常用当前类的加载器去获取指定的类信息ClassLoader classLoader = SelfClassLoaderTest.class.getClassLoader();// 获取当前类的类加载器的父类加载器ClassLoader parentClassLoader = classLoader.getParent();// 根据类的全路径名获取类信息,创建一个	Class 的对象Class loadClass = classLoader.loadClass("com.czp.reflection.Student");// 加载一些配置文件可以使用这个方法InputStream inputStream = ClassLoader.getSystemResourceAsStream("文件名");
}

类加载器加载类时的源码分析:

//根据name查询对应类的Class对象,最终通过jni技术用的c、c++的方法
protected final Class<?> findLoadedClass(String name) {if (!checkName(name))return null;return findLoadedClass0(name);
}
private native final Class<?> findLoadedClass0(String name);//这个方法应该被自定义的类加载器覆盖
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);
}//加载的机制是双亲委托加载
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{//先锁住synchronized (getClassLoadingLock(name)) {// 检查类是否已经加载Class<?> c = findLoadedClass(name);//如果没有加载if (c == null) {try {//判断是否有父类的加载器if (parent != null) {//有父类加载器就用父类加载器去加载c = parent.loadClass(name, false);} else {//没有父类加载器了,则就用启动类加载器去加载c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) { }//如果没有加载到对应的类信息if (c == null) {// findClass方法是一个空方法,所以这里如果使用的是自定义加载器加载类的话,就会试图通过自定义的加载器去获取对应的类信息c = findClass(name);// 定义类装入器;记录数据sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}//将字节数组转换为类类的实例
protected final Class<?> defineClass(String name, byte[] b, int off, int len)  throws ClassFormatError
{protectionDomain = preDefineClass(name, protectionDomain);String source = defineClassSourceLocation(protectionDomain);Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);postDefineClass(c, protectionDomain);return c;
}

如何实现一个自定义类加载器?思路呢就是先读取到.class文件,然后根据读到的数据创建一个Class的对象,正好ClassLoader中已经帮我们实现了,只是需要自定义查找文件的路径,获取到文件的字节流,然后根据提供的defineClass方法将字节转成对应Class对象即可。需要继承ClassLoader类,并重写findClass方法,实现代码如下:

public class MyClassLoader extends ClassLoader{private String path;//在哪个目录下读取.class文件public MyClassLoader(String path) {this.path = path;}@SuppressWarnings("deprecation")@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {byte []b = getClassBinaryData(name);if (b != null) {return defineClass(b, 0, b.length);//将字节数组转陈对应的Class对象} else {throw new ClassNotFoundException();}}//根据路径获取到对应的字节数组private byte[] getClassBinaryData(String className) {String classPath = path + "/" + className.replace(".", "/") +".class";byte[] bs = null;FileInputStream fis = null;ByteArrayOutputStream baos = null;try {fis = new FileInputStream(classPath);baos = new ByteArrayOutputStream();byte[] buffer = new byte[2048];int num = 0;while ((num = fis.read(buffer)) != -1) {baos.write(buffer, 0, num);}bs = baos.toByteArray();} catch (IOException e) {e.printStackTrace();} finally {try {if (fis!=null) {fis.close();}} catch (Exception e2) {}}return bs;}
}-----------------------------测试public static void main(String[] args) {MyClassLoader myClassLoader = new MyClassLoader("E:/");try {Class<?> findClass = myClassLoader.findClass("EnumTest");System.out.println(findClass);} catch (ClassNotFoundException e) {e.printStackTrace();}
}

       这就是类加载器的介绍,这个时候不知是否会有疑问,如果两个加载器要加载的类在两个加载器的路径下均有资源,这要怎么办呢?是两个都添加,然后覆盖,还是咋滴?其实类加载器加载类文件时遵循一种双亲委派模型机制,介绍如下:当一个类加载器收到类加载的请求时,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载,这几个加载器之间的父子关系为:启动类加载器>扩展类加载器>应用程序类加载器>自定义加载器,这里的父子关系并不指的是类上边的extends。

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

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

相关文章

【北京】BXUG第12期活动基于 .NET Core构建微服务和Xamarin

分享主题&#xff1a;基于 .NET Core构建微服务实战分享分享者&#xff1a;薛锋 北京切尔思科技架构师 兼任东北大学信息安全工程师和技术主播&#xff0c;行业内专注于研究 .NET Core和Web应用&#xff0c;具有比较扎实的技术基础和数年的从业经历。在GitHub上主持数个开…

谈谈ASP.NET Core中的ResponseCaching

前言前面的博客谈的大多数都是针对数据的缓存&#xff0c;今天我们来换换口味。来谈谈在ASP.NET Core中的ResponseCaching&#xff0c;与ResponseCaching关联密切的也就是常说的HTTP缓存。在阅读本文内容之前&#xff0c;默认各位有HTTP缓存相关的基础&#xff0c;主要是Cache-…

使用 dynamic 类型让 ASP.NET Core 实现 HATEOAS 结构的 RESTful API

上一篇写的是使用静态基类方法的实现步骤: 使用dynamic (ExpandoObject)的好处就是可以动态组建返回类型, 之前使用的是ViewModel, 如果想返回结果的话, 肯定需要把ViewModel所有的属性都返回, 如果属性比较多, 就有可能造成性能和灵活性等问题. 而使用ExpandoObject(dynamic)就…

使用 BenchmarkDotnet 测试代码性能

先来点题外话&#xff0c;清明节前把工作辞了&#xff08;去 tm 的垃圾团队&#xff0c;各种拉帮结派、勾心斗角&#xff09;。这次找工作就得慢慢找了&#xff0c;不能急了&#xff0c;希望能找到个好团队&#xff0c;好岗位吧。顺便这段时间也算是比较闲&#xff0c;也能学习…

2017西安交大ACM小学期数论 [阅兵式]

阅兵式 发布时间: 2017年6月25日 12:53 最后更新: 2017年7月3日 09:27 时间限制: 1000ms 内存限制: 128M 描述 阅兵式上&#xff0c;将士们排成一个整齐的方阵&#xff0c;每个将士面朝前方。问正中心的将士能向前看到几个将士&#xff1f;注意&#xff0c;一条直线上的将…

28、jdbc操作数据库(5)

介绍一个稍微封装了jdbc的工具类org.apache.commons.dbutils&#xff0c;使用dbutils可以简化对数据库操作程序的开发。 API介绍 接下来通过实例的方式说一下dbutils的具体使用 添加jar包&#xff1a;commons-dbutils-1.7.jar 增、删、改 进行增、删、改操作&#xff0c;在…

2017西安交大ACM小学期数论 [等差数列]

等差数列 发布时间: 2017年6月25日 13:42 最后更新: 2017年7月3日 09:27 时间限制: 1000ms 内存限制: 128M 描述 给定正整数n&#xff0c;试问存在多少个和为n的等差数列&#xff1f; 当然&#xff0c;等差数列中每一项要为非负整数&#xff0c;且不考虑降序的等差数列。…

上古时期(大雾)的数据结构pdf

分块点分治Treap byWYCby\ WYCby WYC Part1 分块 概念 就是将nnn个数分成若干个块&#xff0c;然后要处理的时候整块一起的加上局部的直接暴力。 如果将块的大小分配好一般每次都是O(n)O(\sqrt n)O(n​)的。 而且因为十分暴力&#xff0c;所以有很多优秀的性质。 实现方法 …

33、JAVA_WEB开发基础之会话机制

会话是什么 一个客户端浏览器与web服务器之间连续发生的一系列请求和响应过程就是会话&#xff0c;这些过程中产生的一系列信息就是会话信息&#xff0c;会话机制就是用于维护这些信息一致性的一种技术。通俗的说就是&#xff0c;一个A账号访问服务器&#xff0c;进行多次交互…

35、JAVA_WEB开发基础之过滤器

是什么 过滤器javaweb的一个重要组件&#xff0c;一种规范&#xff0c;可以对发送到serlvet的请求进行拦截和响应进行过滤。实际开发中可以使用过滤器来对访问服务器的请求进行过滤&#xff0c;以提高安全性 过滤器的原理 可以配置过滤器对指定的请求进行过滤&#xff0c;就…

2、安装和连接mysql

安装mysql 1、官网下载mysql 下载网址&#xff1a;https://www.mysql.com/ 2、解压并配置mysql 解压下载的&#xff08;前提下载的zip版本的mysql&#xff09;mysql安装包&#xff0c;放到指定磁盘 配置环境变量&#xff1a;将mysql下的bin目录的全路径名配置到环境变量的p…

6、mysql中字段

对数据表的操作是比较重要的&#xff0c;在实际开发中&#xff0c;日常做的主要工作就是对数据表的操作 对数据表的操作分为两大部分&#xff1a;操作数据表的结构、操作数据表中的数据 组成数据表的基本单元就是字段&#xff0c;所以&#xff0c;接下来先介绍一下mysql中的字…

在Linux环境下使用Apache部署ASP.NET Core

在前几篇文章中我们一起探讨了如何在Linux环境中安装ASP.NET Core运行时环境及将ASP.NET Core项目部署在Jexus中&#xff0c;这篇文章中我们将探讨如何将ASP.NET Core部署于Apache&#xff08;阿帕奇&#xff09;中。 很幸运能够和大家一起学习和探讨ASP.NET Core本文章运行…

傲娇码农的自我修养

一个热爱自己职业的人一定会对自己的工作充满自豪感&#xff0c;同样&#xff0c;也应该对自己的工作充满热情和自信。对自己的专业能力骄傲而不自满。身为一个码农&#xff0c;如果你热爱自己的工作&#xff0c;我想&#xff0c;你很有可能也是一位傲娇码农。在我的眼里&#…

中国到底有多少个.NET 程序员?都在哪个城市写代码?

中国到底多少个.NET 程序员&#xff0c;对于这个问题&#xff0c;似乎没有一个准确的答案&#xff0c;而且最近很多使用.NET 开发技术的老板在抱怨找不到.NET 开发人员&#xff0c;所以我想基于我的公众号粉丝数据给大家分享下中国的.NET程序员到底有多少&#xff0c;他们也都是…

微软西雅图总部DevOps交流总结

本文转自Study4台湾社区。Study4台湾社区&#xff0c;成立于2011/9/25&#xff0c;希望藉由社群推广的力量&#xff0c;让台下的朋友听到来自不同县市的大师讲课&#xff0c;也让台上年轻一辈的技术传教士能不断的琢磨并且追上大师这是一个社群&#xff0c;社区希望透过分享&am…

C# 快速高效率复制对象另一种方式 表达式树

一、需求在代码中经常会遇到需要把对象复制一遍&#xff0c;或者把属性名相同的值复制一遍。比如&#xff1a;public class Student{public int Id { get; set; }public string Name { get; set; } public int Age { get; set; } }public class StudentSecond{public int Id { …

用C# (.NET Core) 实现抽象工厂设计模式

本文的概念性内容来自深入浅出设计模式一书.上一篇文章讲了简单工厂和工厂方法设计模式 使用的是披萨店的例子. 文将继续使用这个例子, 这里要用到抽象工厂.披萨店的需求变更现在披萨店在各地授权了很多连锁分店, 但是有的分店偷工减料, 使用劣质原料代替标准原料.披萨店老板现…

14、mysql中事务的应用

是什么 事务是一种保护连续操作同时满足&#xff08;实现&#xff09;的一种机制&#xff0c;用来保护数据的完整性&#xff0c;只适用于数据操作&#xff0c;不适用于结构操作&#xff0c;只有 innodb引擎的表具有事务安全的机制。就是说&#xff0c;在一个事务中做一系列的…

Summer Training day4 欧拉降幂

Input2 Output2Hint 1. For N 2, S(1) S(2) 1.2. The input file consists of multiple test cases. Sample Input2 Sample Output2这道题的公式非常简单&#xff0c;就是求2^(N-1) %1e97 由于N实在是太大了&#xff0c;不能直接求快速幂&#xff0c;考虑到2^x % MOD是有循…