首先先了解Java对象的创建过程,然后在看看当使用继承时,对象创建过程中的初始化有何不同。
先建立一个父类(基类)Animal:
public class Animal { public Art art1 = new Art(); public static Art art2 = new Art(2); public Animal() { System.out.println("Animal initialized"); } } class Art { public Art() { System.out.println("Art"); } public Art(int i){ System.out.println("Art int"); } }再建立一个子类(导出类、派生类)Dog:
public class Dog extends Animal{ Sub sub1 = new Sub(); static Sub sub2 = new Sub(2); Sub sub3; public Dog() { System.out.println("Dog initialized"); sub3 = new Sub('c'); Sub sub4 = new Sub("D"); System.out.println("Dog initialized again"); } public static void main(String[] args) { // TODO Auto-generated method stub Dog pointTest = new Dog(); } } class Sub{ public Sub() { System.out.println("Sub"); } public Sub(int i ){ System.out.println("Sub int"); } public Sub(char c){ System.out.println("Sub char"); } public Sub(String s){ System.out.println("Sub String"); } }运行结果:
Art int Sub int Art Animal initialized Sub Dog initialized Sub char Sub String Dog initialized again奇怪为什么在创建子类的时候初始化父类,并且还调用了父类的构造函数?
其实,子类在继承父类时不仅仅是复制了父类的接口与变量,当然,子类还能实现自己的接口并覆盖父类的方法。当创建子类对象时,改对象包含了一个父类的子对象,改子对象被包装在子类对象的内部。那该子对象是怎么创建出来的呢?因为我们并能用显示执行父类的构造函数,那Animal initialized是怎么输出的。其实在我们调用子类Dog的构造函数时,Java会自动帮我们在Dog类的构造函数中插入父类Animal的构造函数。但是当基类构造函数带参数时,因为编译器不知道父类构造器需要的参数是什么,所以我们需要主动调用基类的构造函数super(参数)。
接下来分析一下具体的初始化过程:
首先是父类的静态域art2首先被初始化-->然后是子类的静态域sub2初始化-->父类的普通域art初始化-->调用父类的构造函数Animal()-->子类的普通域sub1、sub3按顺序初始化(sub3会被初始化为null)-->最后调用子类的构造函数Dog(),并进行sub3的赋值以及局部变量sub的初始化
当我们注释掉
//Dog pointTest = new Dog();改程序的输出为:
Art int
Sub int
也就是说程序只进行了父类和子类的静态变量的初始化。为什么呢,这是因为没有通过new生成实例对象时,虚拟机只进行了类加载并没有生成对象,因此非静态成员(实例成员)并不会进行初始化,而静态成员(类成员)则被初始化。