一、类加载:
1、类加载:Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码加载到内存,发给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行,最终形成可以被jvm直接使用的Java类型;
1、Bootstrap ClassLoader:负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
2、Extension ClassLoader:负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3、App ClassLoader:负责记载classpath中指定的jar包及目录中class
4、Custom ClassLoader:属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
2、类加载的好处:在java启动的时候运行中完成类、链接、初始化的动作,提高java程序的灵活性。
3、类加载7个阶段:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载
4、类加载时机:
-> 以下5中情况,虚拟机规范则是严格规定了必须立即对类进行初始化
1、new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用类的静态方法的时候。
2、使用java.lang.reflect包的方法对类进行反射调用的时候。
3、初始化一个类的子类的时候。若其父类未被初始化,则先触发父类的初始化。
4、虚拟机执行启动时,被标明为启动类的类(包含main的类)。
5、使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后解析结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
二、类的加载流程:双亲委托
1、一个AppClassLoader查找资源时,先看看缓存是否有,缓存有从缓存中获取,否则委托给父加载器。
2、递归,重复第1部的操作。
3、如果ExtClassLoader也没有加载过,则由Bootstrap ClassLoader出面,它首先查找缓存,如果没有找到的话,就去找自己的规定的路径下,也就是sun.mic.boot.class下面的路径。找到就返回,没有找到,让子加载器自己去找。
4、Bootstrap ClassLoader如果没有查找成功,则ExtClassLoader自己在java.ext.dirs路径中去查找,查找成功就返回,查找不成功,再向下让子加载器找。
5、ExtClassLoader查找不成功,AppClassLoader就自己查找,在java.class.path路径下查找。找到就返回。找不到则抛异常。
三、类的加载顺序:
I、有继承关系的加载顺序:父静态->子静态->父变量->父构造方法->子变量->子构造方法
1.首先加载父类的静态字段或者静态语句块
2.子类的静态字段或静态语句块
3.父类普通变量以及语句块
4.父类构造方法被加载
5.子类变量或者语句块被加载
6.子类构造方法被加载
II、没有继承关系的加载顺序
public class Test3 {
public static void main(String[] args) {
new Test3(); //4.第四步,new一个类,但在new之前要处理匿名代码块
}
static int num = 4; //2.第二步,静态变量和静态代码块的加载顺序由编写先后决定
{
num += 3;
System.out.println("b"); //5.第五步,按照顺序加载匿名代码块,代码块中有打印
}
int a = 5; //6.第六步,按照顺序加载变量
{ // 成员变量第三个
System.out.println("c"); //7.第七步,按照顺序打印c
}
Test3() { // 类的构造函数,第四个加载
System.out.println("d"); //8.第八步,最后加载构造函数,完成对象的建立
}
static { // 3.第三步,静态块,然后执行静态代码块,因为有输出,故打印a
System.out.println("a");
}
static void run() // 静态方法,调用的时候才加载// 注意看,e没有加载
{
System.out.println("e");
}
}