虚拟机规定只有这四种情况才会触发类的初始化(或称类加载,Class Loading),称为对一个类进行主动引用,除此之外所有引用类的方式都不会触发其初始化,称为被动引用。下面举一些例子类加载的几种情况(以下所言的初始化即可理解为类加载)。
class Base { public static int base_a = 10; public static final int base_c = 110; { System.out.println("Base代码块1执行,此时非静态成员base_b =" + this.base_b); } public Base() { System.out.println("Base构造函数执行,此时非静态成员 base_b = " + this.base_b); get(); } public int base_b = 20; { System.out.println("Base代码块2执行,此时非静态成员base_b =" + this.base_b); } static { System.out.println("Base静态代码块执行, 此时静态成员base_a = " + base_a); } public static void f() { System.out.println("Base静态方法"); } public void get() { } } class Super extends Base { // 静态变量、静态块执行顺序,按书写先后顺序 { System.out.println("Super代码块执行"); } static { System.out.println("Super静态代码块执行"); } public static int super_a = getSuperStaticNumber(); public int super_b1 = getSuperInstanceNumber(); public int super_b2 = 100; public Super() { System.out.println("Super构造函数执行,此时非静态成员super_b1 = " + this.super_b1); } public static int getSuperStaticNumber() { System.out.println("Super静态方法被执行,此时静态成员super_a =" + super_a); return 100; } public int getSuperInstanceNumber() { System.out.println("Super非静态方法被执行,此时非静态成员super_b1 = " + this.super_b1); return 200; } public void get() { System.out.println("重载get()方法,此时非静态成员super_b2 = " + this.super_b2); } }1.调用静态字段时,会加载静态字段真正所在的类及其父类,通过子类引用父类中的静态字段,这时对子类的引用为被动引用,因此不会加载子类,只会加载父类。
接口也有初始化过程,上面的代码中我们都是用静态语句块来输出初始化信息的,而在接口中不能使用“static{}”语句块,但编译器仍然会为接口生成<clinit>类构造器,用于初始化接口中定义的成员变量(实际上是static final修饰的全局常量)。接口与类在初始化时最主要的区别是:当一个类在初始化时,要求其父类全部已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量),才会初始化该父接口。这点也与类初始化的情况很不同,回过头来看第2个例子就知道,调用类中的static final常量时并不会 触发该类的初始化,但是调用接口中的static final常量时便会触发该接口的初始化。
类加载的顺序:
1.加载静态成员/静态代码块:public class Test { public static void main(String args[]) { new Super().get(); } }结果:
Base静态代码块执行, 此时静态成员base_a = 10 Super静态代码块执行 Super静态方法被执行,此时静态成员super_a =0 Base代码块1执行,此时非静态成员base_b =0 Base代码块2执行,此时非静态成员base_b =20 Base构造函数执行,此时非静态成员 base_b = 20 重载get()方法,此时非静态成员super_b2 = 0 Super代码块执行 Super非静态方法被执行,此时非静态成员super_b1 = 0 Super构造函数执行,此时非静态成员super_b1 = 200 重载get()方法,此时非静态成员super_b2 = 100
Class baseClass = Base.class;//不会加载Base System.out.println(Base.base_c);//使用了编译期常量static final,不用对类进行加载就可以读取 System.out.println(Base.base_a);//使用了类信息static,非final,对其读取时要先对类进行加载。此时运行结果是:
110 Base静态代码块执行, 此时静态成员base_a = 10 10Base.base_c 是static final,是“编译期常量”,不用对类进行加载就可以读取。而Base.base_a 只是static,对其读取时要先对类进行加载。Class baseClass = Base.class只是创建该类的引用,不会加载该类。Class.forName("Base")会加载。