目录

  • 1 写在前面
  • 2 什么是双亲委派机制
  • 3 类加载器介绍
  • 4 双亲委派机制工作流程
  • 5 双亲委派机制作用
  • 6 双亲委派机制的实现
  • 6 破坏双亲委派模型-线程上下文类加载器
  • 附:面试题:能不能自己写个类叫java.lang.System?

1 写在前面

为了更好的理解双亲委派机制,需要先了解类加载与类加载器相关知识。

2 什么是双亲委派机制

某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,子加载器才会尝试自己去加载。

总结就是

  • 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
  • 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;
  • 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式

3 类加载器介绍

  • Bootstrap 类加载器是用 C++ 实现的,是虚拟机自身的一部分,主要负责加载核心的类库。如果获取它的对象,将会返回 null;

  • 扩展类加载器和应用类加载器是独立于虚拟机外部,为 Java 语言实现的,均继承自抽象类 java.lang.ClassLoader ,开发者可直接使用这两个类加载器。

  • Application 类加载器对象可以由ClassLoader.getSystemClassLoader()方法的返回,所以一般也称它为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

  • 用户自定义类加载器(Custom ClassLoader):可加载指定路径下的class文件。

4 双亲委派机制工作流程

  1. Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。

  2. Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。

  3. 如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载。

  4. 如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。

  5. 如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。

  6. 如果均加载失败,就会抛出ClassNotFoundException异常。

例子:
  当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理会先检查自己是否已经加载过,如果没有再往上。注意这个过程,直到到达Bootstrap classLoader之前,都是没有哪个加载器自己选择加载的。如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException

5 双亲委派机制作用

  1. 避免类的重复加载。当父类加载器加载了该类时,子类加载器就不会再加载一次。

  2. 防止java核心API被随意替换。

假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。可能你会想,如果我们在classpath路径下自定义一个名为java.lang.SingleInterge类(该类是胡编的)呢?该类并不存在java.lang中,经过双亲委托模式,传递到启动类加载器中,由于父类加载器路径下并没有该类,所以不会加载,将反向委托给子类加载器加载,最终会通过系统类加载器加载该类。但是这样做是不允许,因为java.lang是核心API包,需要访问权限,强制加载将会报出如下异常java.lang.SecurityException: Prohibited package name: java.lang

6 双亲委派机制的实现

双亲委派的实现其实并不复杂,其实就是一个递归,我们一起来看一下ClassLoader里的代码:

    // 代码摘自《深入理解Java虚拟机》protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {// 首先,检查请求的类是否已经被加载过了Class c = findLoadedClass(name);if (c == null) {try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// 如果父类加载器抛出ClassNotFoundException// 说明父类加载器无法完成加载请求}if (c == null) {// 在父类加载器无法加载的时候// 再调用本身的findClass方法来进行类加载c = findClass(name);}}if (resolve) {resolveClass(c);}return c;}

6 破坏双亲委派模型-线程上下文类加载器

在某些情况下父类加载器需要委托子类加载器去加载class文件。受到加载范围的限制,父类加载器无法加载到需要的文件,以Driver接口为例,由于Driver接口定义在jdk当中的,而其实现由各个数据库的服务商来提供,比如mysql的就写了MySQL Connector,那么问题就来了,DriverManager(也由jdk提供)要加载各个实现了Driver接口的实现类,然后进行管理,但是DriverManager由启动类加载器加载,只能记载JAVA_HOME的lib下文件,而其实现是由服务商提供的,由系统类加载器加载,这个时候就需要启动类加载器来委托子类来加载Driver实现,从而破坏了双亲委派,这里仅仅是举了破坏双亲委派的其中一个情况。

附:面试题:能不能自己写个类叫java.lang.System?

答案:可以。

我们需要System去打印信息,所以这里换成Math类。

package java.lang;/*** 自定义的一个Math类*/
public class Math {public Math() {System.out.println("Math class init");}
}

在Test类中创建一个实例,调用我们自定义的方式时,会报错Error:(36, 21) java: Math()可以在java.lang.Math中访问private

由于双亲委派机制的存在,我们自定义的String类并不会被加载,所以自然也找不到我们定义的方法。我们这里定义的构造函数是公有的,而原生库中是私有的,所以是非法访问。

如果我们不使用java库中已存在的类名,新起一个名字:MyString,不就可以避开双亲委派吗?

package java.lang;/*** 自定义的java.lang.MyString*/
public class MyMath {public MyMath() {System.out.println("MyString Class init");}
}

测试类中实例化对象,出现异常

Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.langat java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)at java.lang.ClassLoader.defineClass(ClassLoader.java:761)at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)...

查看ClassLoader类中源码中发现,官方不允许我们使用类名以java.开头,所以这种方式行不通。

    private ProtectionDomain preDefineClass(String name,ProtectionDomain pd){if (!checkName(name))throw new NoClassDefFoundError("IllegalName: " + name);// Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias// relies on the fact that spoofing is impossible if a class has a name// of the form "java.*"if ((name != null) && name.startsWith("java.")) {throw new SecurityException("Prohibited package name: " +name.substring(0, name.lastIndexOf('.')));}...}

但是我们可以通过 -Xbootclasspath 参数来指定 Bootstrap加载目录。

参数 效果
-Xbootclasspath: 完全取代核心的Java class 搜索路径。不常用,否则要重新写所有Java 核心class
-Xbootclasspath/p: 前缀在核心class搜索路径前面。也就是优先搜索参数指定路径。不常用,避免引起不必要的冲突.
-Xbootclasspath/a: 后缀在核心class搜索路径后面。也就是其他路径都搜完了,再搜索参数指定路径。常用。

语法:
(分隔符与classpath参数类似,unix使用:号,windows使用;号,这里以unix为例)
java -Xbootclasspath/a:/usrhome/thirdlib.jar: -jar yourJarExe.jar

先把系统的Math类代码复制到我们自己写的Math类中(确保不会因缺少方法报错),再把当前项目打成jar包,最后执行命令java -Xbootclasspath/a:/xxxpath/xxx.jar -verbose > math.txt。查看math.txt文件。

这样,我们确实可以自己编写以"java."开头的代码,但必须交由Bootstrap ClassLoader加载。

Java-双亲委派机制相关推荐

  1. 简单的Java双亲委派机制

    双亲委派机制 Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的 ...

  2. 什么是java双亲委派机制

    什么是双亲委派机制 在介绍双亲委派模型之前先说下类加载器.对于任意一个类,都需要由加载它的类加载器和这个类本身统一确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间.类加载器就是根据 ...

  3. java 委托_面试官:java双亲委派机制及作用

    什么是双亲委派机制 当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类. 类加载器的类别 Bootst ...

  4. 「 JVM基础 」Java双亲委派机制

    Java的双亲委派机制 参考&鸣谢 Dream_ling. weixin_39610188. JVM底层原理解析 文章目录 Java的双亲委派机制 一.介绍 二.什么是双亲委派机制 三.双亲委 ...

  5. JAVA 双亲委派机制

    最近突然看到了关于Java的双亲委派机制,作为一个搞Java的程序猿还是应该知道是怎么回事. 我用一个程序来简单说明是怎么回事吧 package java.util;/*** 本类中的main方法不会 ...

  6. java 双亲委派机制为什么叫“双亲”

    了解了双亲委派机制是什么之后,一直纠结为什么这里要叫"双亲",于是乎上网查了一些资料,大部分都说是翻译的问题,除此之外,其中一篇博客中提到了另一种见解,可以作为参考理解. &quo ...

  7. Java双亲委派机制在Android的应用

    Java的双亲委派机制 三层类加载器 1.启动类加载器(Bootstrap Class Loader) 这个类加载器负责加载存放在<JAVA_HOME>\lib目录,或者被-Xbootcl ...

  8. Java双亲委派机制

    1.什么是双亲委派? 虚拟机在加载类的过程中需要使用类加载器进行加载,而在Java中,类加载器有很多,那么当JVM想要加载一个.class文件的时候,到底应该由哪个类加载器加载呢? 这就不得不提到&q ...

  9. android双亲委派机制,3分钟了解Java双亲委派机制

    你得先知道 在介绍双亲委派机制的时候,不得不提 ClassLoader .说ClassLoader之前,我们得先了解下Java的基本知识. Java是运行在Java的虚拟机(.class 的 JVM中 ...

  10. java spi机制_Java 双亲委派机制的破坏—SPI机制

    作者:程序猿微录 出自:TinyRecord 原文:tinyice.cn/articles/123 Java 双亲委派机制的破坏-SPI机制 在Java的类加载机制中,默认的就是双亲委派机制,这种委派 ...

最新文章

  1. python中异常的姓名
  2. 学会python能找工作吗-Python学到什么程度才可以去找工作?掌握这4点足够了!...
  3. php中的Register Globals
  4. 统计mysql里每条SQL语句执行的时间
  5. (90)AXI突发式读写时序和过程
  6. 删除android电视软件下载,安卓智能电视必装工具没有之一,进程管理备份清理全靠它!...
  7. 女人选择安逸一点还是拼搏一点
  8. 算法-01-二分查找/插值查找
  9. YYKit笔记之FPS
  10. chrome 插件 html代码,【chrome 插件一】开发一个简单chrome浏览器插件
  11. 抢购为什么难,需要怎么做?
  12. 央行二代征信系统即将上线 有哪些变化?
  13. 定理在数学中的简写形式_数学公式定理中的特殊符号含义及读法
  14. 深扒金山云招股书:拆分出来的子公司,能否走出金山系“舒适圈”?
  15. 复旦大学硕士盲审 计算机学院,上海市硕士论文盲审 复旦大学论文抽检、盲审工作的通知...
  16. xmind可以画流程图吗_怎样用XMind方便地制作流程图
  17. python 的math函数库
  18. 网页会屏蔽php代码,网站屏蔽360浏览器访问php源码js代码
  19. Texpad for mac(LaTeX编辑器)激活方法
  20. spring中tx:advice/tx:advice是什么意思?作用是什么?谁能简单说下

热门文章

  1. 假设检验之z-检验,t-检验,卡方检验
  2. 关于NullPointerException空指针异常的解决办法
  3. 几个经典app制作网站
  4. 以太坊开发框架——Truffle的基础使用
  5. melis系统c800_Dell bilgisayarda HDMI bağlantı noktasıyla ilgili Sık Sorulan Sorular (SSS)
  6. Python3 公历转农历
  7. 《冰封王座》世界魔兽界十大叱咤风云人物
  8. 如何给自己的网站接入谷歌联盟
  9. Java学习笔记——从零开始(2018.04.22已更新)
  10. 有史以来最全的CMD命令