Java虚拟机的类加载,从class文件到内存中的类,按先后顺序需要经过加载/链接/初始化三大步骤.
Java语言的类型分为两大类:基本类型(primitive types)和引用类型(reference types).
Java的基本类型,是由Java虚拟机预先定义好的.
Java的引用类型,细分为:类/接口/数组类/泛型参数. 注:泛型参数会在编译过程中被擦除,JVM实际上只有前三种.
加载
加载,是指查找字节流,并且据此创建类的过程. 注:数组类没有对应的字节流,而是由JVM直接生成的,而其它类则需要借助类加载器来完成查找字节流的过程.
双亲委派模型:每当一个类加载器接收到加载请求时,它会将请求转发给父类加载器.在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载.
链接
链接,是指将创建成的类合并至Java虚拟机中,使之能够执行的过程. 分为验证/准备/解析三个阶段.
验证阶段:确保被加载类能够满足Java虚拟机的约束条件.
准备阶段:为被加载类的静态字段分配内存. 注:部分Java虚拟机还会在此阶段构造其它跟类层次相关的数据结构.
解析阶段:将加载过程中对类/字段/方法声明唯一性的符号引用解析成为实际引用.如果符号引用指向一个未被加载的类,或者未被加载的字段或方法,那么解析将触发这个类的加载.
注:Java虚拟机并没有要求在链接过程中完成解析.规定如下:如果某些字节码使用了符号引用,则在执行这些字节码前,需要完成对这些符号引用的解析.
初始化
pre:
如果要初始化一个静态字段,可在声明时直接赋值,亦可在静态代码块中对其赋值.
如果直接赋值的静态字段被final修饰,且类型为基本类型或字符串时,则该字段被Java编译器标记为常量值(Constant Value),初始化直接由Java虚拟机完成.除此之外的直接赋值操作,以及所有静态代码块中的代码,则会被Java编译器置于同一方法中,并命名为<clinit>
初始化,为标记为常量值的字段赋值,以及执行<clinit>方法的过程. 注:Java虚拟机会通过加锁来确保类的<clinit>方法仅被执行一次.
只有当初始化完成之后,类才正式成为可执行的状态.
类初始化的触发时机枚举:
1.当虚拟机启动时,初始化用户指定的主类;
2.当遇到用以新建目标类实例的new指令时,初始化new指令的目标类;
3.当遇到调用静态方法的指令时,初始化该静态方法所在的类;
4.当遇到访问静态字段的指令时,初始化该静态字段所在的类;
5.子类的初始化会触发父类的初始化;
6.如果一个接口定义了default方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;
7.使用反射API对某个类进行反射调用时,初始化这个类;
8.当初次调用MethodHandle实例时,初始化该MethodHandle指向的方法所在的类.