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。

20、java中的类加载机制相关推荐

  1. Java中的类加载机制

    目录 类加载器介绍 JVM类加载过程 类加载器介绍 首先类的加载是由类加载器完成的,类加载器包括:根加载器(Bootstrap).拓展加载器(Extension).系统加载器(System)和用户自定 ...

  2. android classloader异常,Android中ClassLoader类加载机制

    Android中apk的构建过程 构建apk 如图 所示,典型 Android 应用模块的构建流程通常依循下列步骤: 编译器将您的源代码转换成 DEX(Dalvik Executable) 文件(其中 ...

  3. 【面试篇】Java多线程并发-Java中的CAS机制算法

    Java中的CAS机制算法 a.CAS例子 再讲解CAS机制之前,先来看一道经典的并发执行1000次递增的问题: public class Test { public static int count ...

  4. formdata 接受参数中带有class 对象_浅析JAVA中的反射机制及对Servlet的优化

    今天来聊聊java中的反射机制,工作以后发现很多东西动不动就要使用反射或者动态代理,如果不能很好的理解反射,那么对于动态代理等一些重要的设计模式就会有种不够通透的感觉. 所谓的反射,就是在运行状态中, ...

  5. Java中的反射机制详讲

    Java中的反射机制详讲 1.反射机制_介绍_Class对象获取 2.反射机制_动态操作_构造器_方法_属性 3.动态编译_DanamicCompile_反射调用main方法问题 好文推荐:排序.查找 ...

  6. Java和SpringBoot类加载机制

    文章目录 一.SPI介绍 1. SPI机制 2. SPI使用案例 二.Java类加载机制 1.双亲委派模型 2. 双亲委派模型缺陷 3. 使用线程上下文类加载器(ContextClassLoader) ...

  7. 详解Java中的异常机制:运行期异常、编译器异常及如何自定义异常

    文章目录 前言 一.异常概述及分类 1.异常概述 2.异常的继承结构 3.异常的继承机构图 二.运行期异常-RuntimeException 1.JVM如何默认处理异常 2.try...catch的方 ...

  8. Java中的锁机制 -- 乐观锁、悲观锁、自旋锁、可重入锁、读写锁、公平锁、非公平锁、共享锁、独占锁、重量级锁、轻量级锁、偏向锁、分段锁、互斥锁、同步锁、死锁、锁粗化、锁消除

    文章目录 1. Java中的锁机制 1.1 乐观锁 1.2 悲观锁 1.3 自旋锁 1.4 可重入锁(递归锁) 1.5 读写锁 1.6 公平锁 1.7 非公平锁 1.8 共享锁 1.9 独占锁 1.1 ...

  9. 浅说Java中的反射机制(一)

    在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...

最新文章

  1. 震惊了!关于JAVA复习的最佳敏捷实践!进BAT就是个毛毛雨!
  2. python知识体系_python学习知识体系梳理
  3. Kali Linux安装中文输入法
  4. 协作与大数据构建新型打假模式
  5. 关于AJAX访问数据库不能及时获得更新数据的问题
  6. 阿里云容器服务入选云原生边缘「领导力企业TOP3」
  7. 红旗河最早设计计算机的目的,论红旗河的利弊及其替代方案
  8. 春节档总票房已破50亿 情人节单日票房超14亿
  9. 动态规划——最长公共子序列
  10. Leetcode: Increasing Triplet Subsequence
  11. [javascript] Promise API
  12. 路由器命令级别和用户级别
  13. 基于深度学习的商品检索技术
  14. Three.js修改模型中心点
  15. 【openpyxl】插入图表(折线图)
  16. Oracle数据库占用磁盘,导致磁盘活动时间为100%的解决方法
  17. gateway oauth2 对称加密_深入理解Spring Cloud Security OAuth2及JWT
  18. 169-路飞10-redis之列表操作通用操作管道操作
  19. robomongo导入数据_robo 3t - 如何使用robomongo从Mongodb导出json
  20. 春松客服:通过开源加云原生模式,大规模交付智能客服系统 | Chatopera

热门文章

  1. 过去3个多月的1200个小时里,我收获了什么?| 2021年年中总结
  2. [蓝桥杯][历届试题]九宫重排-双向bfs和map标记
  3. How many ways HDU - 1978(记忆化搜索关于求多少种方式模板)
  4. 分布式事务 -- seata框架AT模式实现原理
  5. sql if 和insert_拼多多面试:Mybatis是如何实现SQL语句复用功能的?
  6. ImportError: No module named google.protobuf.internal
  7. 2019-03-11-算法-进化(求众数)
  8. JDK演化系列(1.0~~~1.9)
  9. CF1528C dfs序+set维护
  10. cf1557 C. Moamen and XOR