论坛中已经有很多小伙伴阐述过这个问题,我也用一个例子来说一说
- package cn.itcat.damo1;
- class Animal {
- Animal(){}
- private static int x1 = f("A"); // 1
- static int f(String s) {
- System.out.println(s);
- return 1;
- }
- }
- class Insect extends Animal {
- private int i = 9;
- protected int j;
- public Insect() {
- System.out.println("i=" + i + " j=" + j); // 5
- j = 10;
- }
- private static int x2 = f("B"); // 2
- }
- public class Bettle extends Insect {
- private int k = f("C"); // 6
- public Bettle() {
- System.out.println("k=" + k); // 7
- System.out.println("j=" + j); // 8
- }
- private static int x3 = f("D"); // 3
- public static void main(String[] args) {
- System.out.println("E"); // 4
- Bettle b = new Bettle();
- }
- }
复制代码
在Bettle上运行java时,所发生的第一件事就是试图访问Bettle.main(),于是加载器开始启动并找出Bettle类的编译代码。在对它进行加载的过程中,编译器注意到它有一个基类(父类)-Insect类,于是它继续加载基类,不管你是否打算产生一个该基类的对象,这都要发生。如果该基类还有其自身的基类-Animal类,则会继续加载。
接下来,根基类中的static初始化被执行,然后是下一个导出类(子类),以此类推,所以1,2,3依次执行。
必要的类都已加载完毕,对象就可以被创建了,4执行。首先,对象中所有的基本类型会被设为默认值,对象引用被设为null,也就是所说的默认初始化。然后基类的构造器会被调用,5执行。在本例中,它是自动调用的,也可以通过super来对指定基类构造器的调用。基类构造器和导出类构造器一样,以相同的顺序经历相同的过程。在基类构造器完成以后,实例变量按照次序初始化,6执行。最后,构造器的其余部分被执行,7,8执行。
所以输出结果:A,B,D,E,i=9 j=0,C,k=1j=10
|