构建器的挪用顺序
构建器挪用的顺序已在第4章举办了扼要说明,但那是在担任和多形性问题引入之前说的话。
用于基本类的构建器必定在一个衍生类的构建器中挪用,并且逐渐向上链接,使每个基本类利用的构建器都能获得挪用。之所以要这样做,是由于构建器负有一项非凡任务:查抄工具是否获得了正确的构建。一个衍生类只能会见它本身的成员,不能会见基本类的成员(这些成员凡是都具有private属性)。只有基本类的构建器在初始化本身的元素时才知道正确的要领以及拥有适当的权限。所以,必需令所有构建器都获得挪用,不然整个工具的构建就大概不正确。那正是编译器为什么要强迫对衍生类的每个部门举办构建器挪用的原因。在衍生类的构建器主体中,若我们没有明晰指定对一个基本类构建器的挪用,它就会“冷静”地挪用默认构建器。假如不存在默认构建器,编译器就会陈诉一个错误(若某个类没有构建器,编译器会自动组织一个默认构建器)。
下面让我们看看一个例子,它展示了按构建顺序举办合成、担任以及多形性的结果:
//: Sandwich.java // Order of constructor calls class Meal { Meal() { System.out.println("Meal()"); } } class Bread { Bread() { System.out.println("Bread()"); } } class Cheese { Cheese() { System.out.println("Cheese()"); } } class Lettuce { Lettuce() { System.out.println("Lettuce()"); } } class Lunch extends Meal { Lunch() { System.out.println("Lunch()");} } class PortableLunch extends Lunch { PortableLunch() { System.out.println("PortableLunch()"); } } class Sandwich extends PortableLunch { Bread b = new Bread(); Cheese c = new Cheese(); Lettuce l = new Lettuce(); Sandwich() { System.out.println("Sandwich()"); } public static void main(String[] args) { new Sandwich(); } } ///:~
这个例子在其他类的外部建设了一个巨大的类,并且每个类都有一个构建器对本身举办了公布。个中最重要的类是Sandwich,它反应出了三个级此外担任(若将从Object的默认担任算在内,就是四级)以及三个成员工具。在main()里建设了一个Sandwich工具后,输出功效如下:
Meal() Lunch() PortableLunch() Bread() Cheese() Lettuce() Sandwich()
这意味着对付一个巨大的工具,构建器的挪用遵照下面的顺序:
(1) 挪用基本类构建器。这个步调会不绝反复下去,首先获得构建的是分级布局的根部,然后是下一个衍生类,等等。直到抵达最深一层的衍生类。
(2) 按声明顺序挪用成员初始化模块。
(3) 挪用衍生构建器的主体。
构建器挪用的顺序长短常重要的。举办担任时,我们知道关于基本类的一切,而且能会见基本类的任何public和protected成员。这意味着当我们在衍生类的时候,必需能假定基本类的所有成员都是有效的。回收一种尺度要领,构建动作已经举办,所以工具所有部门的成员均已获得构建。但在构建器内部,必需担保利用的所有成员都已构建。为到达这个要求,独一的步伐就是首先挪用基本类构建器。然后在进入衍生类构建器今后,我们在基本类可以或许会见的所有成员都已获得初始化。另外,所有成员工具(亦即通过合成要领置于类内的工具)在类内举办界说的时候(好比上例中的b,c和l),由于我们应尽大概地对它们举办初始化,所以也应担保构建器内部的所有成员均为有效。若僵持按这一法则行事,会有助于我们确定所有基本类成员以及当前工具的成员工具均已得到正确的初始化。但不幸的是,这种做法并不合用于所有环境,这将在下一节详细说明。