java的继承和初始化及类的加载是个比较复杂的过程,在看java编程思想的时候书上讲到了这部分。书上的一个例子把这个问题讲的很清楚,代码如下:
class Insect { private int i =9; protected int j; Insect() { System.out.println("i="+i+" j="+j); j = 39; } private static int x = printInit("static Insect.x initialized"); static int printInit(String s) { System.out.println(s); return 47; } } public class Beetle extends Insect { private int k = printInit("Bettle.k initialized"); public Beetle() { System.out.println("k="+k+" j="+j); } private static int x2 = printInit("static beetle initialized"); public static void main(String[] args) { System.out.println("bettle constructor"); Beetle b = new Beetle(); }此时程序的输出结果是:
static Insect.x initialized
static beetle initialized
bettle constructor
i=9 j=0
Bettle.k initialized
k=47 j=39
下面具体分析一下程序的执行过程:
我们知道,java文件的运行首先是类的加载,也就是找到main函数然后进行相关类的加载。对于这个java文件,在运行时程序一开始试图找到Beetle的main方法然后加载Beetle的class文件,在class文件加载的过程中,发现这个Beetle类继承了另外一个类Insect所以程序需要先加载Insect的class文件。之后再加载自身的class文件。此时如果Insect类继承了另外一个类时,以同样的方法类推。这里执行完了类的加载过程。但是让人疑惑的是类在加载的时候,具体执行的操作是什么呢?可以通过修改代码进行测试:
class Insect { private int i =9; protected int j; Insect() { System.out.println("i="+i+" j="+j); j = 39; } private static int x = printInit("static Insect.x initialized"); static int printInit(String s) { System.out.println(s); return 47; } } public class Beetle extends Insect { private int k = printInit("Bettle.k initialized"); public Beetle() { System.out.println("k="+k+" j="+j); } private static int x2 = printInit("static beetle initialized"); // public static void main(String[] args) // { // System.out.println("bettle constructor"); // Beetle b = new Beetle(); // } // }上面的代码注释掉了main方法,运行一下看看是什么结果:
static Insect.x initialized
static beetle initialized
java.lang.NoSuchMethodError: main
Exception in thread "main"
程序提示说找不到main方法,那是肯定的因为main方法被注释掉了。然而需要注意的是,没有main方法也就是说程序并未真正执行但程序输出了static Insect.x initialized,static beetle initialized这两句。似乎不可理解。我都没有main方法,怎么会有输出呢?仔细分析一下,首先我们看看输出的这两句有什么特点?不错,他们都是用static修饰的。然后我们再看看他们的输出顺序,Insect.x先输出而beetle后输出。这是为什么呢?这到底是为什么呢?好像有那么点明白了,对,不错,就是上面说到的类加载顺序嘛!理理头绪,首先加载Insect类然后加载Beetle类,而加载时干嘛呢?输出了两个句子,为什么输出者两个句子?因为他们是static修饰的。这下算是彻底搞明白了,程序加载时如果遇到static修饰的部分则直接执行,也就是是static修饰的部分是在编译期执行的!ok,终于明白鸟。
接着我们来分析一下
bettle constructor
i=9 j=0
Bettle.k initialized
k=47 j=39
这些语句是如何输出的。首先是bettle constructor,这点容易理解,因为它是我在main方法中直接打印的。而i=9 j=0是如何输出的呢?看看我们的源程序,找到这个语句的输出位置,发现是在Insect的构造方法中,也就是说在使用Beetle b = new Beetle();声明一个Beetle对象时,首先调用的是父类也就是Insect类的构造方法,此时输出了i和j。而i在声明是直接初始化为9所以输出的是i=9;而j在声明是未初始化,所以使用系统自动初始化:j=0。并且j的值被赋予j=39。在调用了Insect的构造方法后,调用Beetle的构造方法。而Bettle的构造方法中需要输出k的值,所以调用了printInit方法,输出了Bettle.k initialized,k被赋予47于是输出了k=47而此时的j已经在前面被赋予所以输出j=39。至此我们分析完了整个程序的执行流程。
总结一下:程序执行时,首先加载自身的class文件如果这个类继承了另外一个类则先加载父类的class文件之后加载自己的class文件。而在加载class文件时,如果程序中有static修饰的部分则直接执行。(static修饰的部分是编译期部分)在所有类的class文件都加载完后,创建对象时则先使用父类的构造器创建父类的对象,之后使用自己的构造器创建自己的对象。而在创建对象时,各个变量按照出现的顺序依次初始化。