类加载器
以下是一道AK的笔试题,可以很好的理解类加载顺序:
public class ParentTest { static { System.out.println("parent1"); } { System.out.println("parent2"); } ParentTest() { System.out.println("parent3"); } public static void main(String[] args) { ChildTest obj = new ChildTest(); } } class ChildTest extends ParentTest { static { System.out.println("child1"); } { System.out.println("child2"); } ChildTest() { System.out.println("child3"); } }
类加载时就执行静态初始化块,如果有父类的话,先加载父类(只执行父类的静态代码块,其他非静态代码块、静态方法以及非静态方法不执行)。加载完成后,调用构造器的话,会先执行非静态初始化块,再执行构造方法。当然,如果有父类,则先执行父类的非静态初始化块和构造方法,再执行子类的非静态代码块和构造方法。如果不调用构造器,而是直接调用静态方法的话,则非静态初始化块及父类的非静态初始化块不会执行,构造方法和父类的构造方法也不会执行。每个类的静态初始化块都只会被执行一次,在类加载的时候执行。而非静态初始化块可能会执行多次,每调用一次构造器或者子类的构造器,都会执行一次。
所有,上面打印的结果是:
parent1
child1
parent2
parent3
child2
child3
再来一个强化一下:
public class ClassB extends ClassA { static { System.out.println("子类静态代码块 1"); } static { System.out.println("子类静态代码块 2"); } { System.out.println("子类非静态代码块1"); } { System.out.println("子类非静态代码块2"); } public ClassB() { System.out.println("子类构造函数"); } public static void main(String[] args) { System.out.println("....主方法开始...."); new ClassB(); System.out.println("************"); new ClassB(); System.out.println("....主方法结束...."); } } class ClassA { static { System.out.println("父类静态代码块 1"); } static { System.out.println("父类静态代码块 2"); } { System.out.println("父类非静态代码块1"); } { System.out.println("父类非静态代码块2"); } public ClassA() { System.out.println("父类构造函数"); } }
打印什么?这里不再写出来,每次复习的时候直接做这个题目,再跟答案对比,在看文中解释,对理解记忆有帮助。