类加载总共分为以下几步:1、加载(loading);2、验证(verification);3、准备(preparation);4、解析(resolution);5、初始化(initialization);6、使用(using);7、卸载(unloading)
其中2/3/4步统称为连接(linking)。
加载阶段和连接阶段的部分内容是交叉进行的,有可能加载阶段尚未完成,连接阶段就已经开始。但这些连接阶段混杂在加载阶段的内容仍然属于连接阶段。
加载、验证、准备、初始化、卸载这几个阶段会严格按顺序执行,解析可能在初始化之前也可能在初始化之后,这主要是为了支持java的运行时绑定(也称动态绑定或晚期绑定)。
在执对加载这一步的时候,ClassLoader首先会调用父类的loadClass方法,层层网上,直到没有父类。
通过阅读ClassLoader类的源码可以看到,每个ClassLoader都持有一个对父类构造器的引用parent,当loadclass被调用的时候,会首先检查parent引用是否为null,不为null则直接调用parent.loadclass。
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
/**
* Omit a part of code.
*/
try {
if (parent != null) {
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
}
/**
* Omit a part of code.
*/
return c;
}
}
类加载的顺序为:
父类静态变量/块
子类静态变量/块
父类成员变量/块
子类成员变量/块
父类构造器
子类构造器
*每个静态变量/块的加载顺序取决于其在代码中定义的顺序(成员变量/块同理)
一个有趣的问题:
当类中的一个静态引用指向一个实例对象,这个实例对象会在所有静态变量/块完成初始化之前初始化。
public class StaticTest {
static int b = 112;
static StaticTest st = new StaticTest();
static int magicNumber = 666;
}
如该段代码所示,在类加载的过程中,当执行到 static StaticTest st = new StaticTest(); 这一行时,并不会等后续所有的静态变量/块初始化完成再来执行new StaticTest(),而是先执行了new StaticTest(),再执行后面的静态变量初始化。