大家好,我是伍六七。
今天我们来聊聊一个 Java 面试必考基础题目:类加载机制和双亲委派机制。
Java 类的加载机制是 Java 虚拟机(JVM
)中类加载(Class Loading
)和链接(Linking
)的过程的一部分。这个过程遵循以下步骤:
1、加载(Loading
): 这是第一步,其中 JVM 会通过类的全名找到这个类的二进制数据流,并从这个数据流中构造出一个 Class 对象。
2、链接(Linking
): 链接过程包含验证(Verify)、准备(Prepare)和解析(Resolve)三个阶段。
- 验证: 确保被加载的类符合 JVM 规范。
- 准备: 为类的静态变量分配内存,并将其初始化为默认值。
- 解析: 虚拟机将类中的符号引用转换为直接引用。
3、初始化(Initialization
): 这个阶段是执行类构造器 <clinit>()
方法的过程,这个方法由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}
块)中的语句合并产生的。
Java 类加载机制的核心是双亲委派模型。
在这个模型中,一个类加载器在加载类时,首先会请求其父类加载器加载该类,依此类推,直到顶层的启动类加载器。如果父类加载器无法完成这个加载任务(即它没有找到对应的类),子类加载器才会尝试自己去加载这个类。
这句话,我们经常看到,那什么意思呢?用大白话告诉你:
我们需要知道,默认情况,Java 提供了以下三类类加载器:
1、启动类加载器(Bootstrap ClassLoader):
这是类加载器层级结构中的最顶层加载器。它负责加载 JVM 的核心类库,如 java.lang.*。
2、扩展类加载器(Extension ClassLoader):
它是启动类加载器的子类。负责加载 Java 的扩展类库,这些类库通常位于 jre/lib/ext
目录或由系统属性 java.ext.dirs
指定的任何其他目录中。
3、应用程序类加载器(Application ClassLoader):
这是扩展类加载器的子类。
它负责加载环境变量 classpath
或系统属性 java.class.path
指定路径中的类。
这种机制有助于防止类的重复加载,并保证了 Java 核心库的类型安全。
详细解释具体流程
1、类加载请求的初始处理
当一个类加载器(比如应用程序类加载器)接收到一个请求,用来加载一个特定的类时,它并不会立即尝试去加载这个类。
2、委派给父类加载器
代替自行加载,这个类加载器会将加载请求向上委派给它的父类加载器。例如,应用程序类加载器会先将请求委派给扩展类加载器,扩展类加载器再将请求委派给启动类加载器。
3、父类加载器的处理
父类加载器(在这个例子中是启动类加载器)会尝试加载这个类。启动类加载器主要负责加载 Java 的核心类库(例如 java.lang.*)。
如果它能够找到并加载这个类,那么加载过程到此结束,子类加载器不会再进行任何操作。
4、父类加载器无法加载时的回退
如果父类加载器(比如启动类加载器)无法加载该类(通常是因为它在其搜索路径下没有找到相应的类),那么它会将请求回退给请求它的子类加载器(在这个例子中是扩展类加载器)。
接着,扩展类加载器会尝试自己去加载这个类。如果扩展类加载器也无法加载这个类,同样的过程会发生在它和它的子类加载器(应用程序类加载器)之间。
5、保障类加载的有序性和安全性
这种委派机制确保了 Java 核心类库的类型不会被随意替换,因为自定义的类加载器无法替换那些由启动类加载器加载的核心类库。
同时,它也避免了类的重复加载,因为在尝试自行加载类之前,每个类加载器都会先检查它的父类加载器是否已经加载了该类。
通过这种方式,双亲委派模型为 Java 类的加载提供了一种层次化和有序的方法,保证了类加载机制的健壯性和安全性。