类加载阶段

类加载阶段

1. 加载

加载: 是指将编辑器编译后的java文件加载到jvm中,其中加载又分为几个阶段。

  1. ClassLoader 通过全路径的形式通过 双亲委派机制 加载class文件二进制字节流到内存里面.

  2. 二进制字节流转换为虚拟机需要的格式存储在方法区之中。

  3. 在内存中共生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据结构的访问入口。

2.连接

  1. 验证
    因为class文件是十六进制的文件,jvm为了确保该文件不会对自身产生安全隐患,会对其做一层验证,比如:
    class文件的魔数(cafebabe),版本号等等。
    2.准备
    在准备阶段,jvm会将final标记的属性完成初始化(包括赋值),static标记的属性也会完成初始化(但不包括赋值),比如:
// 这个阶段就会将 a 的值赋值成1。
final int a = 1;

// 这个阶段会将a初始化为0。
static int a = 1;

3.解析
该阶段会将Class文件的符号引用转换成直接引用
如:

类:Java.lang.String
对应描述符:Ljava/lang/String;

int num=3;
字段描述符:I;
字段名:num

String[][] num=null;
字段描述符:[[Ljava/lang/String;
字段名:num

String test();
方法描述符:()Ljava/lang/String;

long test(int i, Object c);
方法描述符:(ILjava/lang/Object;)J

void test(byte[] bytes);
方法描述符:([B)V

初始化

这一步主要就是执行静态变量的初始化,包括静态变量的赋值和静态初始化块的执行。也就是上述static修饰的变量 a 在这里被赋值

引入一个新概念类构造器方法

  • < clinit >()方法是由编译器自动收集类中的所有类变量的赋值动作和静态初始化块中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定的,静态初始化块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量静态语句块中可以赋值但是不能访问(这里有一点需要注意,静态初始化块和静态变量赋值语句执行顺序是按定义顺序来的,并不是说初始化块一定会在静态赋值语句之后执行)。

  • < clinit >()方法与类的构造函数不同,它不需要显示的调用父类的()方法,虚拟机会保证在子类的< < clinit >()方法执行之前,父类的< clinit>()方法已经执行完毕。因此在虚拟机中第一个被执行的< clinit>()方法的类肯定是java.lang.Object。

  • < clinit >()方法对于类或接口来说不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成< clinit>()方法。
    接口中不能使用静态初始化块,但是仍有static变量的赋值操作,所以也会有< clinit >()方法,但是接口执行

  • < clinit >()方法不需要先执行父接口的< clinit>()方法。只有当父接口中定义的变量被使用到时,才会执行< clinit >()方法。虚拟机会保证一个类的< clinit>()方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的< clinit>()方法,其它线程都需要阻塞等待。

©️2020 CSDN 皮肤主题: 数字50 设计师:CSDN官方博客 返回首页