双亲委派机制是常见的面试题。今天我们就一起了解一下。
在讲双亲委派机制之前,首先了解一下类加载器。
全文的思维导图:
类加载器
类加载器的分类
- JVM支持两种类型的类加载器。分别为
引导类加载器
和自定义类加载器
类加载器的介绍
启动类加载器(引导类加载器 Bootstrap ClassLoader)
- 这个类加载使用
C/C++语言
实现的,嵌套在JVM内部 - 它用来
加载Java的核心库
(JAVA_HOME / jre / lib / rt.jar、resources.jar 或 sun.boot.class.path 路径下的内容),用于提供JVM自身需要的类 - 并不继承自
java.lang.ClassLoader
,没有父加载器 - 加载扩展类和应用程序类加载器,并作为他们的父类加载器
- 出于安全考虑,Bootstrap启动类加载器只加载包名为
java、javax、sun
等开头的类
扩展类加载器(Extension ClassLoader)
Java语言编写
,由sun.misc.Launcher$ExtClassLoader
实现- 派生于
ClassLoader
类 - 父类加载器为启动类加载器
- 从
java.ext.dirs
系统属性所指定的目录中加载类库,或从JDK的安装目录的 jre / lib / ext子目录(扩展目录)下加载类库。如果用户创建的 JAR 放在此目录下,也会自动由扩展类加载器加载
应用程序类加载器(系统类加载器,AppClassLoader)
Java语言编写
,由sun.misc.LaunchersAppClassLoader实现- 派生于ClassLoader类
- 父类加载器为扩展类加载器
- 它负责加载环境变量
classpath
或系统属性java.class.path
指定路径下的类库 该类加载是程序中默认的类加载器
,一般来说,Java应用的类都是由它来完成加载的- 通过
classLoader.getSystemclassLoader( )
方法可以获取到该类加载器
类加载器流程
双亲委派机制
什么是双亲委派机制?
在类加载的时候,系统会首先判断当前类是否加载过,已经加载的类会直接返回,否则会尝试加载,加载的时候不会先尝试加载这个类,而是把请求委派给你父类加载器。每一层加载都是这样,因此所有的请求最终都会到达顶层的启动类加载器,只有当父类无法处理,子类才会尝试自己去加载,当父类的加载器为null,会使用启动类加载器 BoostrapClassLoader作为父类加载器。
好处
- 双亲委派模型保证了java程序的稳定执行,避免了类的重复加载.
- 保证了核心API的不被篡改。
怎么理解核心API不被篡改呐?
下面我们举个例子来做演示:
我们自己定义一个java.lang包,在其下面定义一个String类,里面声明了静态代码块
package java.lang;
/**
* @author yanshuang
* @date 2022/1/14 1:22 下午
*/
public class String {
static {
System.out.println("我是自定义的String类的静态代码块");
}
}
在一个测试类中加载String类,看看加载的String类是JDK自带的,还是我们自己编写的
package com.yan.java;
/**
* @author yanshuang
* @date 2022/1/14 1:23 下午
*/
public class StringTest {
public static void main(String[] args) {
String s = new java.lang.String();
}
}
结果:程序并没有输出我们静态代码块中的内容,可见仍然加载的是 JDK 自带的 String 类
接下来我们再举个例子
在我们自己定义的 String 类中整个 main( ) 方法
package java.lang;
/**
* @author yanshuang
* @date 2022/1/14 1:22 下午
*/
public class String {
static {
System.out.println("我是自定义的String类的静态代码块");
}
//错误: 在类 java.lang.String 中找不到 main 方法
public static void main(String[] args) {
System.out.println("hello,String");
}
}
结果:程序并没有输出我们静态代码块中的内容,可见仍然加载的是 JDK 自带的 String 类
原因:由于双亲委派机制,我们的String类是由引导类加载器加载的(String为什么是引导类加载器加载的上面解释了),而引导类加载器并没有main方法,所有会报错