首先先了解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生成实例对象时,虚拟机只进行了类加载并没有生成对象,因此非静态成员(实例成员)并不会进行初始化,而静态成员(类成员)则被初始化。