字节码指令和符号引用、直接引用
1.主动引用和被动引用
主动引用:虚拟机规定只有满足四个情况的的情况下,才会进行主动引用。
被动引用:除过四种情况的引用是被动引用。
只有主动引用才会初始化
2.通过子类调用父类的静态字段,是被动引用,不会让初始化子类,只会初始化父类。
对于静态字段,只有直接定义这个字段的类会才会被初始化,因此,通过子类调用父类的静态字段,只会让父类初始化,子类不会初始化
class Father { public static int age = 54; static { System.out.println("父类被初始化"); } } class Child extends Father{ static { System.out.println("子类被初始化"); } } class Test{ public static void main(String args[]){ syso(Child.g=age); } } 输出:父类被初始化 33
3.常量在编译阶段会存入调用它的类的常量池中,本质上没有直接引用到定义该常量的类上,因此不会触发定义该常量的类。
对于final对象,考虑编译阶段是否是符号引用还是直接引用
class Const{ public static final int String name = "常量"; static{ syso("初始化Const类"); } } class test{ public static void main(String args[]){ syso(Const.name); } } 输出:常量 虽然在程序中引用了Const类的常量,但是在编译阶段将此常量的值“常量”存储到了test类的常量池中,对Const常量的引用实际转化成了对自身常量池的引用。两个类在编译之后就不会存在联系了。
4.通过数组定义来引用类,不会触发类的初始化
class Const{ public static final int String name = "常量"; static{ syso("初始化Const类"); } } class test{ public static void main(String args[]){ Const sonst[] = new Const[5]; } } 输出 不输出任何内容 在这段代码里面触发了另一个名为“LLConst”的类的初始化,他是一个由虚拟机自动生成、直接继承与java.lang.Object的子类,创建动作有字节码指令newarray触发,很明显,这是由一个对数组引用 类型的初始化,而该数组只包含了一个队Const的引用,并没有对其进行初始化,如果加入实例化数组里面的Const对象,就会触发 class test{ public static void main(String args[]){ Const sonst[] = new Const[5]; for(Const s: sonst){ s = new Const(); } } 输出:Const被初始化
5.接口和类在初始化的不同:
接口也有初始化的过程,上面的代码中都是用静态语句块的输出初始化信息的,在接口中不能使用“static{}”语句块,但编译器仍然会为接口生成<clinet>类构造器用于初始化接口定义的成员变量;
当一个类在初始化时,要求其父类全部已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部完成了初始化,只有真正使用父接口的时候(如引用接口定义的常量),才会初始化该父类接口。