[Java] 实例创建的步骤
创建类的一个实例时,按照下面步骤进行创建:
1. 给当前类及其父、祖类的所有成员字段分配空间,并给它们赋予默认值
2. 开始执行当前类的构造器
3. 如果当前类有父类,则对父类创建一个实例:从第 2 步开始并把父类当作新的当前类
4. 给当前实例、当前实例的字段进行初始化
5. 执行当前类的构造器的剩余部分代码。
上面的步骤包含一个递归算法。假设类 C 有父类,实例化 C 到第 3 步的时候,需要先创建父类的一个实例,即父类要经历完第 2、3、4、5 步骤之后,才到类 C 继续第 4 步。如果父类也有自己的父类,则父类的父类也要先实例化,才到父类执行第 3 步。依次类推,直到实例化 Object 类,因为 Object 类没有父类。
下面有两个例子进行说明。
1,常规的类实例化
public class Point { int x, y; Point(){ x = 1; y = 1; } }
public class ColoredPoint extends Point { int color = 0xFF00FF; }
public class InstanceDemo { public static void main(){ ColoredPoint cp = new ColoredPoint(); System.out.println(cp.x + " | " + cp.y + " | " + cp.color); } }
引起创建类的实例的是代码 new ColoredPoint(),下面是实例创建的步骤:
1. 分配内存给字段 x, y, color, 并给他们赋予默认值
2. ColoredPoint 的构造器被调用
3. ColoredPoint 没有声明构造器,则调用其默认构造器:
ColoredPoint(){ super(); }
4. 上面的 super() 实际指的是 Point 的无参构造器。Point 的无参构造器最开始部分没有调用其他构造器,则 Java 编辑器提供插入了一个构造器调用,变成:
Point(){ super(); x = 1; y = 1; }
5. 上面的 super() 实际指的是 Object 的无参构造器。Object 没有父类,不会被插入构造器。Object 的无参构造器没有任何副作用或返回值。被调用的构造器如下
Object() { }
6. Point 的字段被初始化。在这里,字段 x, y 无需被初始化。
7. Point 的构造器被继续执行,即对 x , y 分别赋值为 1 。至此,父类 Point 实例化结束
8. ColoredPoint 的字段被初始化。在这里,字段 color 被初始化为 0xFF00FF。
9. ColoredPoint 的构造器被继续执行。在这里,没有其他代码在构造器里面,无需继续执行。至此,ColoredPoint 实例化结束。
2,动态调度在实例化期间的影响
Super,作为父类,在构造器中调用函数 printStatus()。注意,printStatus() 函数被子类重写,所以这里调用的不是 Super 自身的 PrintStatus(),而是其子类的 printStatus().
Sub, 继承于 Super 类,在构造器中调用 printStatus(),然后对变量 age 重写赋值。
InstanceDDDemo, 演示在实例化期间,方法动态调度的影响。
具体代码:
public class Super { public Super(){ System.out.println("in Super() "); printStatus(); } public void printStatus(){ System.out.println("Super.printStatus - "); } }
public class Sub extends Super{
private int age = 10; public Sub(){ System.out.println("in Sub() "); printStatus(); age = 20; } public void printStatus(){ System.out.println("Sub.printStatus - " + age); } }
public class InstanceDDDemo { public static void main(){ Sub point = new Sub(); System.out.println(" After instance "); point.printStatus(); } }
输出:
in Super() Sub.printStatus - 0 in Sub() Sub.printStatus - 10 After instance Sub.printStatus - 20
从输出可见,在执行 Sub 构造里面的代码之前,Sub.printStatus() 已经被调用过一次。
Java 支持方法的动态调度。动态调度是指在运行时才决定选择哪一个多态实现方法。虽然 Super 有自己的 printStutus() 放方法,但是本例子中,Super 构造函数的调用,是由于它被 Sub 类继承,并且 Sub 类在被实例化,所以,这里最原始的目的是创建 Sub 类实例,而 printStutas() 方法对于 Sub 类来说已经被重写了,故在调用父类 Super 的构造器中仍然调用被重写后的 Sub.printStatus().
参考资料
12.5 Creation of New Class Instance, The Java Language Specification, Java SE 8 Edition