目录

一、概述

二、ClassLoader的主要方法

三、SecureClassLoader与URLClassLoader

四、ExtClassLoader与AppClassLoader

五、Class.forName()与ClassLoader.loadClass()

六、 loadClass()方法解析


一、概述

ClassLoader类继承关系图如下:

除了以上虚拟机自带的加载器外,用户还可以定制自己的类加载器。Java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器都应该继承ClassLoader类。

二、ClassLoader的主要方法

ClassLoader内部没有抽象方法,主要的方法如下:

【a】返回该类加载器的超类加载器

public final ClassLoader getParent()

【b】加载名称为name的类,返回结果为java.lang.Class类的实例。如果找不到类,则返回 ClassNotFoundException异常。该方法中的逻辑就是双亲委派模式的实现。

public Class<?> loadClass(String name) throws ClassNotFoundException

【c】查找二进制名称为name的类,返回结果为java.lang.Class类的实例。这是一个受保护的方法,JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,该方法会在检查完父类加载器之后被loadClass()方法调用。

protected Class<?> findClass(String name) throws ClassNotFoundException
  • 在JDK1.2之前,在自定义类加载时,总会去继承ClassLoader类并重写loadClass方法,从而实现自定义的类加载类。但是在JDK1.2之后已不再建议用户去覆盖loadClass()方法,而是建议把自定义的类加载逻辑写在findClass()方法中,从前面的分析可知,findClass()方法是在loadClass()方法中被调用的,当loadClass()方法中父加载器加载失败后,则会调用自己的findClass()方法来完成类加载,这样就可以保证自定义的类加载器也符合双亲委托模式。
  • 需要注意的是ClassLoader类中并没有实现findClass()方法的具体代码逻辑,取而代之的是抛出ClassNotFoundException异常,同时应该知道的是findClass方法通常是和defineClass方法一起使用的。

一般情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载加载规则,取得要加载类的字节码后转换为流,然后调用defineClass()方法生成对应的Class对象。

【d】根据给定的字节数组b转换为Class的实例,off和len参数表示实际Class信息在byte数组中的位置和长度,其中byte数组b是ClassLoader从外部获取的。这是受保护的方法,只有在自定义ClassLoader子类中可以使用。

protected final Class<?> defineClass(String name, byte[] b,int off,int len)
  • defineClass()方法:用来将byte字节流解析成JVM能够识别的Class对象(ClassLoader中已实现该方法逻辑),通过这个方法不仅能够通过class文件实例化class对象,也可以通过其他方式实例化class对象,如通过网络接收一个类的字节码,然后转换为byte字节流创建对应的Class对象。
protected Class<?> findClass(String name) throws ClassNotFoundException {// 获取类的字节数组byte[] classData = getClassData(name);if (classData == null) {throw new ClassNotFoundException();} else{//使用defineClass生成Class对象return defineClass(name,classData,θ,classData.length);}
}

【e】链接指定的一个Java类。使用该方法可以使用类的Class对象创建完成的同时也被解析。前面我们说链接阶段主要是对字节码进行验证,为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用。

protected final void resolveClass(Class<?> c)

【f】查找名称为name的已经被加载过的类,返回结果为java.lang.Class类的实例。这个方法是final方法,无法被修改。

protected final Class<?> findLoadedClass(String name)

【g】重要属性:超类加载器

private final ClassLoader parent;

parent属性也是一个ClassLoader的实例,这个字段所表示的是这个ClassLoader的双亲。在类加载的过程中,ClassLoader可能会将某些请求交予自己的双亲处理(双亲委派机制)。

三、SecureClassLoader与URLClassLoader

SecureClassLoader扩展了ClassLoader,新增了几个与使用相关的代码源(对代码源的位置及其证书的验证)和权限定义类验证(主要指对class源码的访问权限)的方法,一般我们不会直接跟这个类打交道,更多是与它的子类URLClassLoader有所关联。

前面说过,ClassLoader是一个抽象类,很多方法是空的没有实现,比如findClass()、findResource()等。而URLClassLoader这个实现类为这些方法提供了具体的实现,并新增了URLClassPath类协助取得Class字节码流等功能。

在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,

这样就可以避免自己去编写findClass()方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。

四、ExtClassLoader与AppClassLoader

了解完URLClassLoader后接着看看剩余的两个类加载器,即拓展类加载器ExtClassLoader和系统类加载器AppClassLoader,这两个类都继承自URLClassLoader,是sun.misc.Launcher的静态内部类。

sun.misc.Launcher主要被系统用于启动主应用程序,ExtClassLoader和AppClassLoader都是由sun.misc.Launcher创建的,其类主要类结构如下:

我们发现ExtClassLoader并没有重写loadClass()方法,说明其遵循双亲委派模式,而AppClassLoader重载了loadClass()方法,但最终调用的还是父类loadClass()方法,因此依然遵守双亲委派模式。

五、Class.forName()与ClassLoader.loadClass()

  • Class.forName():是一个静态方法,最常用的是Class.forName(String className)

根据传入的类的全限定名返回一个Class对象。该方法在将Class文件加载到内存的同时,会执行类的初始化

Class.forName("com.wsh.jvm.test.Helloworld");
  • ClassLoader.loadClass():这是一个实例方法,需要一个ClassLoader对象来调用该方法

该方法将Class文件加载到内存时,并不会执行类的初始化,直到这个类第一次使用时才进行初始化。该方法因为需要得到一个ClassLoader对象,所以可以根据需要指定使用哪个类加载器。

Classloader cl = ......;
cl.loadClass("com.wsh.jvm.test.Helloworld");

六、 loadClass()方法解析

public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{//加锁,如果多个线程进行加载,只会有一个线程能够加载成功synchronized (getClassLoadingLock(name)) {//检查此类是否已经被加载了,尝试从缓存中获取Class对象,如果获取到说明已经加载,如果为空,说明此类未加载Class<?> c = findLoadedClass(name);//此类未加载if (c == null) {long t0 = System.nanoTime();/try {//判断当前类加载器的父类加载器是否为空if (parent != null) {//父类加载器不为空,调用父类的loadClass方法进行加载(注意,这里是递归)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}//如果还是没能加载,则调用findClass进行加载if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}//是否需要解析if (resolve) {resolveClass(c);}//如果从缓存中获取到Class,说明此类已经加载过,则直接返回即可return c;}}

ClassLoader类解析相关推荐

  1. java.lang包—类加载器ClassLoader类

    注意: 类加载器的知识与JVM内存模型紧密相连,要学好这块的知识,一定要掌握JVM的内存模型. 关于JVM内存模型,推荐阅读:JVM-内存模型JMM 目录 一.什么是ClassLoader? 二.类加 ...

  2. @param注解的用法解析_SpringBoot 配置类解析

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/NvPO5-FWLiOlrsOf4wLaJA 作者:Li Wanghong SpringBoot ...

  3. RandomAccessFile类解析

    [0]README 0.1) 本文描述转自 core java volume 2, 旨在理解 java流与文件--RandomAccessFile类解析 的相关知识: 0.1) 本文 转自: http ...

  4. JAVA调试出现不断在ClassLoader类中执行时的问题?

    在Eclipse中进行JAVA调试时,出现不断在ClassLoader类中执行时的问题? 解决方法: (1)打开window - Show view - Other - Debug - Breakpo ...

  5. 深入类加载器-类加载器作用,类缓存、类加载器的层次结构、ClassLoader类介绍、代理模式之双亲委派机制

    1.类加载器的作用 类加载器的作用是将class字节码文件加载到内存中,并将这些静态数据转化为方法区中的运行时数据结构,同时在堆中生成代表这个类的java.lang.Class对象,作为访问方法区中数 ...

  6. Android开发--使用实体类解析JSON文本

    Android开发–使用实体类解析JSON文本 在Android开发过程中,涉及到了API的调用的时候,会返回特定的数据,两个主流返回的数据格式是JSON形式和XML形式.但是相对于XML,JSON数 ...

  7. Java String、StringBuffer、StringBuilder类解析

    String.StringBuffer.StringBuilder类解析 概述 String类:代表字符串. 特点 String实现了Serializable接口,表示String是可序列化的 实现了 ...

  8. Cloudsim 3.0.3中VM调度策略系列类解析(带迁移的策略)

    Cloudsim 3.0.3中VM调度策略系列类解析(带迁移的策略) 注:本文为旧文的markdown重制版 Cloudsim中VM调度策略类在DataCenter(或PowerDataCenter) ...

  9. Queue常用类解析之BlockingQueue(二):ArrayBlockingQueue

    Queue常用类解析之PriorityQueue Queue常用类解析之ConcurrentLinkedQueue Queue常用类解析之BlockingQueue(一):PriorityBlocki ...

  10. Spring Boot 整合 SpringDataNeo4j 并封装工具类解析PathValue

    Spring Boot 整合 SpringDataNeo4j 并封装工具类解析PathValue 一.Neo4j 二.Neo4j客户端浏览器 三.maven依赖 四.节点/关系映射 1.NodePer ...

最新文章

  1. 近亿台物联网设备或遭劫持,这家IoT云平台遭遇“灾难性”入侵事件
  2. Spring Boot 启动,1 秒搞定!
  3. 关于Linux系统中用户权限问题
  4. MKL学习——向量操作
  5. Date扩展 正则匹配
  6. 2017.4.11 AM
  7. 离开小米后 周受资将加入字节跳动担任CFO
  8. 如何判定括号是否匹配
  9. ABBYY PDF Transformer+功能概述
  10. CentOS 6.5安装YouCompleteMe使用vim C/C++语法自动补全
  11. greasyfork脚本怎么取消_greasy fork脚本大全电脑游戏安装使用
  12. sklearn.impute.SimpleImputer 数据填充
  13. 数显之家快讯:「SHIO世硕心语」2021年,中国不可错过的5大红利!
  14. Python os.symlink创建软链接
  15. UTF8与GBK字符编码之间的相互转换
  16. excel冻结窗口怎么设置_excel打印区域怎么设置?excel表格打印区域怎么设置?
  17. 美光科技股价上涨13% 创下自2011年12月以来最大单日涨幅
  18. 数据库mysql中对于drop_数据库之删除表数据drop、truncate和delete的用法
  19. MySQL 中 You can‘t specify target table ‘表名‘ for update in FROM clause解决办法
  20. 阿里百秀前后端交互项目

热门文章

  1. 小米3c虚拟服务器,小米路由器3C固件逆向与测评-新手向
  2. 阿里云云计算 41 阿里云CDN的工作原理
  3. 阿里云云计算 36 PolarDB MySQL的管理步骤
  4. 2021-09-07Hadoop运行模式:
  5. linux变量接收命令返回值,linux shell自定义函数(定义、返回值、变量作用域)介绍...
  6. 概率图模型(PGM)学习笔记(五)——模板模型
  7. org.apache.hadoop.hbase.mapreduce.Driver 导入数据到HBASE table
  8. 机器学习数学基础之微分
  9. android .9横向拉伸,神奇的问题!android .9图片拉伸不是不会变形吗?但是这里变形了...
  10. mysql如何进行宿舍分配_手把手教你做一个Jsp Servlet Mysql实现的学生宿舍管理系统...