接着上一份笔记,继续学习类加载部分:
2.立即初始化类的五种情况:
a.遇到new、getstatic、putstatic或invokestatic这4条字节码指令会触发其初始化;对应场景是使用new关键字、读取或设置一个类的静态字段(被final修饰,已在编译器期把几个放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候
b.使用java.lang.reflect包的方法对类进行反射调用的时候,触发初始化
c.当初始化类的时候,发现父类还没有进行过初始化时候,则需要先触发其父类的初始化
d.当虚拟机启动时候,用户u需要指定一个要执行的主类(包含main方法的类),虚拟机会优先初始化那个类
e.jdk1.7新方法动态语言获取到了几个关键字的句柄,触发初始化
3.在类加载的时候,介绍了几种被动引用的例子,不会导致初始化
a.子类引用了父类的静态字段,只会读父类进行初始化,不会对子类初始化,因为只会加载直接定义了这个静态字段的类才会被初始化
b.new 一个数组类型的对象,不会导致初始化,数组类型的这种形式是由虚拟机直接生成的
c.使用了一个被final修饰,且已经被加载到常量池中的字段也不会触发定义这个常量的类的初始化,因为jvm在编译时候有优化,相当于直接指向了该类的常量池中
针对初始化这一块,说一下自己的理解,不是说所有的类都会进行初始化的,注意深入理解jvm上有解释,是有且只有这五种情况,才会触发类的初始化,这种叫做主动引用;
假如类里面没有这五种任意一种情况,就不会触发初始化, 因此在理解的时候,我们学习jvm这块不要模糊,认为所有的类都会进行初始化,对一些不理解的就会认为是虚拟机自己有操作,是虚拟机已经执行了这些代码之类的,其实不是
举个例子:
class SingleTon {
private static SingleTon singleTon = new SingleTon();
public static int count1;
public static int count2 = 0;
private SingleTon() {
count1++;
count2++;
}
public static SingleTon getInstance() {
return singleTon;
}
}
public class Test {
public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
System.out.println("count1=" + singleTon.count1);
System.out.println("count2=" + singleTon.count2);
}
}
输出了什么?
首先看, SingleTon.getInstance();调用了类的静态方法,属于五种情况的第三种,触发类的初始化开始执行类的初始化方法:clinit()
a..按照静态代码顺序:
private static SingleTon singleTon = new SingleTon();
public static int count1;
public static int count2 = 0;
public static SingleTon getInstance() {
return singleTon;
}
count1和count2在类加载的准备阶段被赋初值为 0
b.首先执行private static SingleTon singleTon = new SingleTon(); 触发实例的初始化,开始执行实例的初始化方法,按照父类非静态变量出现顺序-父类构造器-子类非静态代码 - 子类构造器执行构造器执行完毕代表实例初始化完毕,回归类的初始化
d.count2被重新赋值为0;
e.回归Test中方法:count1=1;count2=0