类的加载分为以下几个步骤:
1、加载:查找并加载类的二进制字节码
类的加载是将类的. class文件 中的二进制字节码读到内存当中,此时做了两个非常重要的事情:
①将类的信息放在方法区 ( jdk 1.8后称为元空间) (方法区里的class文件信息包括:魔数,版本号,常量池,类,父类和接口数组,字段,方法等信息)
②在堆空间创建一个代表这个类的 java .lang.Class的对象 (Class对象对于反射而言很有用)
2、链接
①验证
确保被加载类的正确性 (类的信息是否符合jvm规范,没有安全方面的问题等)
②准备
为类的类变量( static 静态变量)分配内存,并将其初始化为默认值 (static int i = 2,初始化阶段默认值是0;final int i = 2,i是常量,准备阶段直接分配内存并且设置为目标值,所以准备阶段赋值为2), 类变量随着class对象一起分配到堆中;注意,此时并没有为实例变量分配内存空间,实例变量会随着创建实例的时候一起在堆中分配
③解析
将类中的符号引用变为直接引用。 主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符等7类符号引用
符号引用:用任意形式字面量表示所引用的目标
直接引用:可以指向目标的指针,偏移量或句柄等
3、初始化
为类的静态变量赋予正确的初始值 (static int = 2,准备阶段是0,次阶段赋值为2)
java程序对类的使用方法可分为两种 主动使用(会导致类的初始化)和被动使用
主动调用(6种)
①创建类的实例
②访问某个类或者接口的静态变量,或者对该静态变量赋值(final除外)
③调用类的 静态方法
④反射 (如Class.forName(“com.XXXX.XXX”))
⑤初始化一个类的子类
⑥ Java虚拟机 启动时被标明为启动类的类
除了以上的6种情况,其他使用java类的方式都被看做对类的被动使用,不会导致类的初始化
被动使用
①当访问一个静态字段时,只有真正声明这个字段的类才会被初始化( 当通过子类引用父类的静态变量,不会导致子类初始化 )
②通过数组定义类引用,不会触发此类的初始化 (User[] users= new User[10] )
③引用常量不会触发此类或接口的初始化。因为常量在链接阶段就已经被显示赋值了
④调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。