类的加载是指将class字节码文件加载到虚拟机内存中,总共分为5个阶段:
1、加载
将字节码文件从外部存储系统(本地磁盘,文件系统,网络)加载到虚拟机内存的过程,这是进入虚拟机的第一步。
2、连接
连接是整个加载器最耗时的阶段,连接又可以细分为三个阶段:
2.1、验证:对加载进来的class字节码进行格式,语法等合法性验证
2.2、准备:对类型的静态变量进行设置默认值,此处是设置默认值(整型为0,布尔为false,实例引用为null),等到使用阶段才真正赋值用户定义的值。
2.3、解析:将常量池的符号引用替换成直接引用,符号引用:以一组符号描述所引用的目标,可以是形式的字面量,无歧义定位就行。直接引用:指向目标对象的指针。
对静态变量的赋值语句进行赋值操作
条件:
当类首次主动使用时,虚拟机才会对该类型进行初始化操作
何为主动使用(类的使用可以分为主动使用和被动使用):
1、创建类的实例对象
2、调用类的静态成员变量或者给静态变量赋值。
3、调用类的静态方法
4、反射,使用Class.forName("com.demo.Test")
5、启动时,指定main方法所在的类
6、子类使用时,对应的父类也需要初始化
7、jdk1.7以后的动态机制(类似于动态代理):Jsp、动态代理
除了上述7种方式,其它的使用都是被动使用。
1、编译时期的常量(final修饰)
编译时,虚拟机已经将使用该常量的类,把常量值放到了该类的常量池中,因此常量的访问,不会产生定义常量类的初始化动作。
2、运行时期的常量(final修饰)
编译时期不能确定的常量,只能通过运行时期虚拟机进行获取值,因此需要经过类的初始化过程,如果定义了该常量的类还有父类,也相对应的初始化父类。
3、接口的初始化,首先有2个结论
3.1、子接口的初始化,并不会初始化其父接口,但是父接口可能会被加载进内存,因为加载和初始化是两个不同的步骤。
3.2、类的初始化,不会初始化其实现的接口。
接口的变量默认就是public、static和final的,所以直接访问编译期的变量时,不会经过接口的初始化动作。
只有当首次主动使用到父接口时,才会对父接口进行初始化。
因此对接口和类的初始化的汇总如下:
A:当子类真正被使用时,子类会被初始化,对应的父类也会被初始化,但是接口除外;对于常量,只有运行期才知道值的常量,类才会需要初始化。
B:针对类而言:对于常量,如果是编译期就定义好了,那么调用变量不会引起类的初始化;运行期间才知道的值常量,则会引起初始化,并且遵循子类等待父类初始化结束的原则。
4、使用
5、卸载