对象的创建过程和初始化
实际上,前面提到的“变量初始化发生在调用任何方法包括构造方法之前”这句话是不确切的,当我们把眼光集中在单个类上时,该说法成立;然而,当把视野扩大到具有继承关系的两个或多个类上时,该说法不成立。
对象的创建一般有两种方式,一种是用new操作符,另一种是在一个Class对象上调用newInstance方法;其创建和初始化的实际过程是一样的:
首先为对象分配内存空间,包括其所有父类的可见或不可见的变量的空间,并初始化这些变量为默认值,如int类型为0,boolean类型为false,对象类型为null;
然后用下述5个步骤来初始化这个新对象:
1)分配参数给指定的构造方法;
2)如果这个指定的构造方法的第一个语句是用this指针显式地调用本类的其它构造方法,则递归执行这5个步骤;如果执行过程正常则跳到步骤5;
3)如果构造方法的第一个语句没有显式调用本类的其它构造方法,并且本类不是Object类(Object是所有其它类的祖先),则调用显式(用super指针)或隐式地指定的父类的构造方法,递归执行这5个步骤;如果执行过程正常则跳到步骤5;
4)按照变量在类内的定义顺序来初始化本类的变量,如果执行过程正常则跳到步骤5;
5)执行这个构造方法中余下的语句,如果执行过程正常则过程结束。
这一过程可以从下面的时序图中获得更清晰的认识:
对分析本文的实例最重要的,用一句话说,就是“父类的构造方法调用发生在子类的变量初始化之前”。可以用下面的例子来证明:
// Petstore.java class Animal { Animal() { System.out.println("Animal"); } } class Cat extends Animal { Cat() { System.out.println("Cat"); } } class Store { Store() { System.out.println("Store"); } } public class Petstore extends Store{ Cat cat = new Cat(); Petstore() { System.out.println("Petstore"); } public static void main(String[] args) { new Petstore(); } } |
运行这段代码,它的执行结果如下:
Store
Animal
Cat
Petstore
从结果中可以看出,在创建一个Petstore类的实例时,首先调用了它的父类Store的构造方法;然后试图创建并初始化变量cat;在创建cat时,首先调用了Cat类的父类Animal的构造方法;其后才是Cat的构造方法主体,最后才是Petstore类的构造方法的主体。