类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
类初始化是类加载过程的最后一个阶段,到初始化阶段,才真正开始执行类中的Java程序代码。虚拟机规范严格规定了有且只有5种情况必须立即对类进行初始化:
第一种:遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类还没有进行过初始化,则需要先触发其初始化。生成这四条指令最常见的Java代码场景是:使用new关键字实例化对象时、读取或设置一个类的静态字段(static)时(被static修饰又被final修饰的,已在编译期把结果放入常量池的静态字段除外)、以及调用一个类的静态方法时。
第二种:使用Java.lang.refect包的方法对类进行反射调用时,如果类还没有进行过初始化,则需要先触发其初始化。
第三种:当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
第四种:当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先执行该主类。
第五种:当使用JDK1.5支持时,如果一个java.langl.incoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始
执行代码:
package com.spring.partise;import java.util.Random;class A{static final int numA = Main.rand.nextInt(100);static{System.out.println("I am A");}
}
class B{static final int numB = 2;static{System.out.println("I am B");}
}
class C{static int numC = 3;static{System.out.println("I am C");}
}
public class Main {static Random rand = new Random();public static void main(String[] args) throws Exception {System.out.println("---------------------------");System.out.println(A.numA);//会执行static中的方法System.out.println("---------------------------");System.out.println(B.numB);//不会执行static中的方法System.out.println("---------------------------");System.out.println(C.numC);}
}
执行结果:
---------------------------
I am A
22
---------------------------
2
---------------------------
I am C
3
总结:如果一个static final值是“编译期常量”,就像static final int numB = 2;那样,那么这个值不需要对B类进行初始化就可以读取。但是,如果只是将一个域设置为static和final的,那不一足以确保这种行为,例如,对static final int numA = Main.rand.nextInt(100);的访问将强制进行类的初始化,因为它不是一个编译期常量。
如果一个static 域不是final,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间)就像static int numC = 3;那样!!!