时间:2021-05-20
本文实例讲述了Java中构造器内部的多态方法的行为操作。分享给大家供大家参考,具体如下:
这篇文章主要讨论的是,若在一个构造器中调用正在构造的对象的某个动态绑定的方法时会出现的情况。在此之前,我们需要知道构造器是如何在复杂的层次结构中运作的,尽管构造方法并不具有多态性,因为它们实际上是static方法,只不过是隐式声明的static。
复杂层次结构中构造器的调用顺序
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。这样做是因为,在Java类中,我们通常将字段设置为private类型,也就是说,在子类中通常无法直接访问基类的字段,那么只有通过调用基类的构造器才能对基类对象的元素进行初始化,那么就必须保证所有的构造器都得到调用,这样才能正确地构造完整的对象。下面的例1展示了包含有组合与继承关系的各类中构造器的调用顺序:
例1:
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()");}}public class Sandwich extends PortableLunch { private Bread b = new Bread(); private Cheese c = new Cheese(); private Lettuce l = new Lettuce(); public Sandwich() { System.out.println("Sandwich()"); } public static void main(String[] args) { new Sandwich(); }}例1反映了关于Meal、Lunch和Sandwich之间三层继承关系(不包含Object类),以及Bread、Cheese和Lettuce与Sandwich的组合关系。在main函数中创建一个Sandwich对象后,我们就可以看到输出结果:
这说明在复杂的层次结构中构造器的调用遵从的顺序为:
** 1、调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,直到最低层的导出类;
2、按声明的顺序调用成员的初始化方法;
3、调用导出类构造器的主体。**
构造器内部的多态方法的行为
那么,现在我们回到文章开头提到的问题,若在一个构造器中调用正在构造的对象的某个动态绑定的方法,会出现什么情况呢?我们知道,动态绑定(或后期绑定)的方法的调用是在运行时才决定的,因为对象在程序运行之前无从得知它自己到底是基类的对象,还是某个导出类的对象。如果在基类的构造器内部调用某个动态绑定方法,该方法是被导出类覆盖的,那么这便可能产生难以预料的后果,因为该导出类的对象还未被完全构造,但它的方法却被调用了。我们可以通过例2看到问题所在:
例2:
class Glyph { void draw() { System.out.println("Glyph.draw()"); } Glyph() { System.out.println("Glyph() before draw()"); draw(); System.out.println("Glyph() after draw()"); }} class RoundGlyph extends Glyph { private int radius = 1; RoundGlyph(int r) { radius = r; System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius); } void draw() { System.out.println("RoundGlyph.draw(), radius = " + radius); }} public class PolyConstructors { public static void main(String[] args) { new RoundGlyph(5); }}运行结果:
在运行结果中,我们看到,基类Glyph的构造器中调用了被子类RoundGlyph覆盖的draw()方法,并且输出了radius=0,这显然是一个错误,因为这个“0”是在其他任何事物发生之前,系统分配给对象的存储空间的初始值——二进制的零,而非我们想要设定的初始值“1”。这是因为,我们在创建子类(RoundGlyph)对象时会先调用基类(Glyph)的构造器构造基类对象,而在基类的构造器中却调用了被子类覆盖的动态绑定的方法(draw()),而这个方法所操纵的可能是子类中的还未进行初始化的成员(radius),这便会招致灾难,尽管编译器并没有报错。
因此,在编写构造器中有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法”。在构造器中,唯一能够安全调用的是基类中的final方法(包括private方法),因为这些方法不能被子类覆盖,也就不会出现上述的问题。
更多java相关内容感兴趣的读者可查看本站专题:《Java面向对象程序设计入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
Collections工具类Java里关于聚合的工具类,包含有各种有关集合操作的静态多态方法,不能实例化(把构造函数私有化)publicclassCollect
本文实例讲述了Java中的多态用法。分享给大家供大家参考。具体分析如下:多态,是面向对象的程序设计语言最核心的特征。封装性、继承性都比较简单,所以这里只对多态做
本文实例讲述了java继承中的构造方法。分享给大家供大家参考。具体如下:继承中的构造方法:1、子类的构造过程中必须调用其基类的构造方法。2、子类可以在自己的构造
本文实例讲述了C#中多态现象和多态的实现方法。分享给大家供大家参考。具体分析如下:面向对象的特征封装、继承和多态。Polymorphism(多态性)来源于希腊单
工厂方法(FactoryMethod)模式又称为虚拟构造器(VirtualConstructor)模式或者多态工厂(PolymorphicFactory)模式,