类的整个生命周期:
- 加载
- 验证
- 准备
- 解析
- 初始化
- 使用
- 卸载
验证,准备,解析又统称连接。
加载,验证,准备,初始化,卸载这5个阶段总是按顺序“开始”(而不是顺序进行或者完成)
有且只有5种情况必须立即对类进行初始化
- 遇到new、getstatic、putstatic、invokestatic这4个字节码指令,如果类没有被初始化,则立即初始化。常见场景是new对象,读取静态字段(final修饰的除外,编译器已经加入调用类的常量池),调用一个类的静态方法时。
- 使用java.lang.reflect包的方法对类进行反射调用时。
- 当初始化一个类的时候,如果其父类还没有初始化,则先触发其父类初始化。
- 当虚拟机启动时,虚拟机会先初始化指定包含main()方法的那个类。
- JDK1.7增加动态语言的支持。如果一个
java.lang.invoke.MethodHandle
实例最后的解析结果是REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,而这个句柄所在的类没有进行初始化,则需要先触发初始化。
除了这5个场景,其他引用类的方式都不会触发其初始化。如
- 子类.父类静态属性(或方法) //会初始化父类,不会初始化子类
- SomeClass[] sc = new SomeClass[10];//触发一个名为“[L”+SomeClass全类名 的类的初始化阶段。由虚拟机自动生成,字节码指令为newarray
- 调用static final 修饰的常量
在准备阶段,变量被赋初始值
<client>() 方法是由编译器自动收集类中的所有类变量的赋值动作和static{}块中的语句合并产生的,按源文件代码顺序收集。
static{}块中只能访问定义在其之前的变量,定义在其后的变量可以赋值,但不能访问。
<client>()方法由虚拟机保证其父类的<client>()方法已经执行完毕。
<client>()方法由虚拟机保全其在多线程的情况下被正确的加锁,同步,只有一个线程会去执行初始化操作,其他的线程处于阻塞状态。
public class DeadLoopClass {
static{
if(true){//没有if的话会initializer must be able to complete normally
while (true){
System.out.println(Thread.currentThread()+"looping");
}
}
}
}
public class TestDeadLoopClass {
public static void main(String[] args) {
Runnable script1 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread()+"start");
DeadLoopClass dlc = new DeadLoopClass();
System.out.println(Thread.currentThread()+"over");
}
};
Thread t1 = new Thread(script1);
Thread t2 = new Thread(script1);
t1.start();
t2.start();
}
}
Thread[Thread-1,5,main]start
Thread[Thread-0,5,main]start
Thread[Thread-1,5,main]looping
Thread[Thread-1,5,main]looping
Thread[Thread-1,5,main]looping