注:本文摘自On Java8
构造器调用的层次结构带来了一个困境 。如果在构造器中调用了正在构造的对象的动态绑定方法 , 会发生什么呢?
在普通的方法中 , 动态绑定的调用是在运行时解析的 , 因为对象不知道它属于方法所在的类还是类的派生类 。
【构造器内部多态方法的行为】如果在构造器中调用了动态绑定方法 , 就会用到那个方法的重写定义 。然而 , 调用的结果难以预料因为被重写的方法在对象被完全构造出来之前已经被调用 , 这使得一些 bug 很隐蔽 , 难以发现 。
从概念上讲 , 构造器的工作就是创建对象(这并非是平常的工作) 。在构造器内部 , 整个对象可能只是部分形成——只知道基类对象已经初始化 。如果构造器只是构造对象过程中的一个步骤 , 且构造的对象所属的类是从构造器所属的类派生出的 , 那么派生部分在当前构造器被调用时还没有初始化 。然而 , 一个动态绑定的方法调用向外深入到继承层次结构中 , 它可以调用派生类的方法 。如果你在构造器中这么做 , 就可能调用一个方法 , 该方法操纵的成员可能还没有初始化——这肯定会带来灾难 。
下面例子展示了这个问题:
// polymorphism/PolyConstructors.java// Constructors and polymorphism// don't produce what you might expectclass 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);}@Overridevoid draw() {System.out.println("RoundGlyph.draw(), radius = " + radius);}}public class PolyConstructors {public static void main(String[] args) {new RoundGlyph(5);}}输出:
Glyph() before draw()RoundGlyph.draw(), radius = 0Glyph() after draw()RoundGlyph.RoundGlyph(), radius = 5Glyph 的 draw() 被设计为可重写 , 在 RoundGlyph 这个方法被重写 。但是 Glyph 的构造器里调用了这个方法 , 结果调用了 RoundGlyph 的 draw() 方法 , 这看起来正是我们的目的 。输出结果表明 , 当 Glyph 构造器调用了 draw() 时 , radius 的值不是默认初始值 1 而是 0 。这可能会导致在屏幕上只画了一个点或干脆什么都不画 , 于是我们只能干瞪眼 , 试图找到程序不工作的原因 。
初始化的实际过程是:
- 在所有事发生前 , 分配给对象的存储空间会被初始化为二进制 0 。
- 如前所述调用基类构造器 。此时调用重写后的
draw()方法(是的 , 在调用 RoundGraph 构造器之前调用) , 由步骤 1 可知 , radius 的值为 0 。 - 按声明顺序初始化成员 。
- 最终调用派生类的构造器 。
另一方面 , 应该震惊于输出结果 。逻辑方面我们已经做得非常完美 , 然而行为仍不可思议的错了 , 编译器也没有报错(C++ 在这种情况下会产生更加合理的行为) 。像这样的 bug 很容易被忽略 , 需要花很长时间才能发现 。
因此 , 编写构造器有一条良好规范:做尽量少的事让对象进入良好状态 。如果有可能的话 , 尽量不要调用类中的任何方法 。在基类的构造器中能安全调用的只有基类的 final 方法(这也适用于可被看作是 final 的 private 方法) 。这些方法不能被重写 , 因此不会产生意想不到的结果 。你可能无法永远遵循这条规范 , 但应该朝着它努力 。
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
