- 论坛徽章:
- 0
|
动态邦定调用是在运行的时候决定的,因此无法决定他是属于方法所在的那个类,还是属于那个类的导出类。为了保持一致,大家也许会认为这应该发生在构造器的内部,其实不然,如果要调用构造器内部的一个动态邦定的方法,就要用到那个方法的被覆盖后的定义。然而,产生的效果是相当难以预料的,并且可能造成难以发现的隐藏错误。
构造器的工作实际上就是创建对象。在任何构造器的内部,整个对象可能只是部分形成-----我们只知道基类的对象已经进行初始化,但却不知道哪些类是从我们这里继承而来的。然而一个动态邦定的方法调用却会向外深入到继承层次结构内部,它可以调用导出类的方法。如果在构造器内部这样做,那么就可能会调用某个方法,而这个方法所操作的成员可能还没有被初始化---这样肯定会导致错误。
通过下面的例子我们已可看到错误所在:
//: c07:PolyConstructors.java
// From 'Thinking in Java, 2nd ed.' by Bruce Eckel
// www.BruceEckel.com. See copyright notice in CopyRight.txt.
// Constructors and polymorphism
// don't produce what you might expect.
abstract class Glyph {
abstract void draw();
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
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() before draw()
RoundGlyph.draw(),radius=0;
Glyph() after draw();
RoundGlyph.RoundGlyph() radius=5;
在Glyph中draw()方法是抽象的,这样设计是为了覆盖该方法。在RoundGlyph类中我们覆盖了这个方法,但是Glyph构造器会调用这个方法,结构导致了对RoundGlyph.draw()的调用。但是输出的结构显示当Glyph的构造器调用draw()方法时候,radius不是默认初始值1而是0。该如何解释呢?
我们回到初始化:
1):在其他任何事情发生之前,将分配给对象的储存空间将初始化成二进制的零。
2):如前面所示那样调用基类的构造器,此时调用被覆盖的draw()方法,由于第一步骤地执行,我们回发现radius的初始值变成了0。
3):按照申明的顺序调用成员的初始化。
4):调用导出类的构造器主体。
这样做的一个优点就是将所有的东西都至少初始出成为了0,而不是留作垃圾。如果忘记了对饮用的初始化,纳闷就会在运行的时候出现异常。
因此编写构造器的时候有一条准则:尽可能用简单的方法使对象进入正常状态,如果可以得花,避免调用其他方法。在构造器唯一能能够安全调用的那些方法是基类中的final方法。这些方法不会被覆盖,自然不会出现令我们惊讶的问题了。
------《java编程思想》
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/21684/showart_431201.html |
|