使用继承时,子类对象的初始化过程

时间:2023-02-15 19:37:08

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