文章目录

  • jdk的类加载器
  • 双亲委派
  • 自定义类加载器
    • 打破双亲委派

jdk的类加载器

查看一个类的类加载器:

 ClassLoader classLoader = boy.class.getClassLoader();

查看父加载器:

ClassLoader classLoader = boy.class.getClassLoader().getParent();

如果父加载器是根加载器,打印的是null
例如

ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();ClassLoader extClassloader = appClassLoader.getParent();ClassLoader bootstrapLoader = extClassloader.getParent();System.out.println("the bootstrapLoader : " + bootstrapLoader);System.out.println("the extClassloader : " + extClassloader);System.out.println("the appClassLoader : " + appClassLoader);

查看classLoader 的loadClass源码:

public abstract class ClassLoader {//加载具有指定二进制名称的类。此方法的默认实现按以下顺序搜索类:调用 findLoadedClass(String) 以检查该类是否已加载。在父类加载器上调用 loadClass 方法。如果 parent 为 null,则使用虚拟机内置的类加载器。调用 findClass(String) 方法来查找 class
private final ClassLoader parent;//parent不是继承关系,而是作为成员变量protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {//首先检查是否已经加载过Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found//从非空父类加载器}if (c == null) {// 如果仍未找到,则调用 findClass 以查找该类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);}return c;}}

loadClass的类加载过程有如下几步:
加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载
加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
加载完成后,Class 对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证(校验字节码文件)、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM 对类进行初始化,包括:

1 如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;

2 如果类中存在初始化语句,就依次执行这些初始化语句。

类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader 的子类)。从JDK 1.2 开始,类加载过程采取了父亲委托机制(PDM)。PDM 更好的保证了Java 平台的安全性,在该机制中,JVM 自带的Bootstrap 是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM 不会向Java 程序提供对Bootstrap 的引用。

注意,主类在运行过程中如果使用到其它类,会逐步加载这些类。
jar包或war包里的类不是一次性全部加载的,是使用到时才加载。

双亲委派

当“应用程序类加载器"接到一个加载任务时
(1)先搜素内存中是否已经加載过了,如果加载 过了,就可以找到对应的C1ass对象,那么就不加载了
(2)如 果没有找到,把这个任务先提交给 parent",父加载器接到任务时,也是重复(1)(2)
3)直到传给了根加载器,如果根加载器可以加载,就完成了,如果不能加戟,往回传,依次每 个加載器尝试在自己负责的路径下搜素,如果找到就直接返回Class对象,如果一直回传到“应用程序类加载器”,还是没有找到就会报ClassnotFoundException

为什么用这种机制:

为了安全,防止你写一个核心类。
每一个加载器只负责自己路径下的东西。

类加载器的作用
1、最主要的作用 :加载类
2、辅助的作用:可以用它 来加载“类路径下”的资源文件
例如:bin中src下文件ー->bin目录下Properties.Propertie s类表示了一个持久的属性集, Properties可保存在流中或从中加载属性列表中每个键
举个例子,ClassLoader 加载的class 文件来源很多,比如编译器编译生成的class、或者网络下载的字节码。

而一些来源的class 文件是不可靠的,比如我可以自定义一个java.lang.Integer 类来覆盖jdk 中默认的Integer

例如下面这样:

 package java.lang;public class Integer {public Integer(int value) {System.exit(0);
}
}

初始化这个Integer 的构造器是会退出JVM,破坏应用程序的正常进行,如果使用双亲委派机制的话该Integer 类永远不会被调用,以为委托BootStrapClassLoader 加载后会加载JDK 中的Integer 类而不会加载自定义的这个

自定义类加载器

public class MyClassLoader extends ClassLoader{private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}protected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}}public static void main(String args[]) throws Exception {//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:/test");//D盘创建 test/com/test/jvm 几级目录,将User类的复制类User1.class丢入该目录Class clazz = classLoader.loadClass("com.test.jvm.User1");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}

打破双亲委派

public class MyClassLoaderTest {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}protected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}/*** 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载* @param name* @param resolve* @return* @throws ClassNotFoundException*/protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);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.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}if (resolve) {resolveClass(c);}return c;}}}public static void main(String args[]) throws Exception {MyClassLoader classLoader = new MyClassLoader("D:/test");//尝试用自己改写类加载机制去加载自己写的java.lang.String.classClass clazz = classLoader.loadClass("java.lang.String");Object obj = clazz.newInstance();Method method= clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}运行结果:
java.lang.SecurityException: Prohibited package name: java.langat java.lang.ClassLoader.preDefineClass(ClassLoader.java:659)at java.lang.ClassLoader.defineClass(ClassLoader.java:758)

类加载器源码、双亲委派、自定义类加载器详解相关推荐

  1. Caddy源码阅读(一)Run详解

    Caddy源码阅读(一)Run详解 前言 本次系列会讲解 caddy 整个生命周期涉及到的源码. 平时我们使用 caddy 都是使用 它的 二进制 分发文件,现在来分析 caddy 的 Run 函数. ...

  2. Mybatis源码学习(三)SqlSession详解

    前言 上一章节我们学习了SqlSessionFactory的源码,SqlSessionFactory中的方法都是围绕着SqlSession来的.,那么SqlSession又是什么东东呢?这一章节我们就 ...

  3. centos7 mysql 源码安装_CentOS7.4 源码安装MySQL8.0的教程详解

    MySQL 8 正式版 8.0.11 已发布,官方表示 MySQL 8 要比 MySQL 5.7 快 2 倍,还带来了大量的改进和更快的性能! 以下为本人2018.4.23日安装过程的记录.整个过程大 ...

  4. 仿抖音短视频APP源码,顶部导航栏切换详解

    仿抖音短视频APP源码,顶部导航栏切换详解的相关代码 class DaoHangNan extends StatefulWidget //继承StatefulWidget{TabController ...

  5. 双亲委派模型以及SpringFactoriesLoader详解(最全最简单的介绍)

    文章目录 前言 类加载的过程 类加载器 何为双亲委派模型 ClassLoader类的loadClass方法 双亲委派模型存在的问题 解决办法 以JDBC驱动管理为例 加载资源 SpringFactor ...

  6. 【流媒体开发】VLC Media Player - Android 平台源码编译 与 二次开发详解 (提供详细800M下载好的编译源码及eclipse可调试播放器源码下载)

    作者 : 韩曙亮  博客地址 : http://blog.csdn.net/shulianghan/article/details/42707293 转载请注明出处 : http://blog.csd ...

  7. 拍卖源码java_Java并发的AQS原理详解

    原文:https://juejin.im/post/5c11d6376fb9a049e82b6253 每一个 Java 的高级程序员在体验过多线程程序开发之后,都需要问自己一个问题,Java 内置的锁 ...

  8. bytebuddy实现原理分析 源码分析 (三)- advice 详解

    advice详解 八.advice 8.1 AsmVisitorWrapper 8.1.1 ForDeclareFields 8.1.1.1 Entry 8.1.1.2 DispatchingVisi ...

  9. linux maven编译代码,git拉源码maven构建后部署脚本详解

    部署某个中间件到测试机器脚本 从git取源码,再maven构建,将jar拷贝到服务器,备份旧版本,重启. #!/bin/bash server_ips=(132.121.100.44 132.121. ...

  10. php5模块怎么下载,centos源码编译php5 mcrypt模块步骤详解

    步骤: 1.从php.net上面下载php5.3.x版本的源码; 2.centos安装相应的扩展包: 代码如下: yum install libmcrypt libmcrypt-devel mcryp ...

最新文章

  1. DBA大牛告诉你,如何让MySQL语句执行加速?
  2. hibernate mysql id 自增长 注解_hibernate Mysql 自增长 注解配置,表无关联的注解方式关联查询...
  3. 肝!计算机网络基础知识总结
  4. Android下集成Paypal支付
  5. 一步一步写算法(之循环和递归)
  6. Redis-数据结构02-简单动态字符串(sds)
  7. Kubernetes面试题
  8. 系统集成项目管理工程师 案例题【2021上】 总结
  9. java 牙位图插件_牙医的骄傲-智能牙位图中文医疗应用app全球排名No.1
  10. C++程序员常用工具集
  11. Mac下生成SSH key
  12. Android 手机横屏时,输入框铺满全屏的解决方案
  13. 2010年国家公务员考试行测真题WORD完整版
  14. LTE(4G) ATTACH流程
  15. 一位博士在华为的22年(干货满满)
  16. html onload状态事件,HTML onload事件用法及代码示例
  17. ArcGIS的运行许可文件ecp如何打开?
  18. 微信公众号对接网课查题系统
  19. R语言进行神经网络算法——RSNNS
  20. 消防设施操作员考试真题、模拟练习题库(8)

热门文章

  1. content_scripts css,chrome 扩展开发 中 content_scripts 配置的 文件没有执行?
  2. java 二维数组作为参数传递_java JNI 二维数组作为方法参数传递给本地
  3. echarts指针进度条刻度调整_指针式流量开关
  4. python callback函数_回调函数callbacks
  5. asp ed什么意思 j_这部洗脑ED动画是如何创作出来的?
  6. php5.1 0day,DEDECMS 5.1 feedback_js.php 0DAY
  7. Oracle查找包共用,oracle – 用于查找包的多级依赖关系的脚本
  8. AHP层次分析法解决用户价值评估
  9. 7. webpack 初步熟悉使用
  10. 深圳内推 | ​IDEA数字经济研究院招聘NLP算法工程师/算法实习生