第八章 多态

[color=blue]方法调用绑定[/color]

将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定,叫做前期绑定。读者可能以前从来没有听说过这个术语,因为它是面向过程的语言中不需要选择就默认的绑定方式。

上面问题的解决办法就是后期绑定,它的含义就是在运行时根据对象的类型进行绑定。后期绑定也就做动态绑定或运行时绑定。如果一种语言想实现后期绑定,就必须具有某种机制,一遍在运行时能判断对象的类型,从而调用恰当的方法。也就是说,编译器一直不知道对象的类型,但是方法的调用机制能找到正确的方法体,并加以调用。后期绑定机制随编程语言的不同而有所不同,但是只要想一下就会得知,不管怎样都必须在对象中安置某种“类型信息”。

Java中除了static方法和final方法之外,其他所有的方法都是后期绑定。这意味着通常情况下,我们不必判定是否该进行后期绑定—它会自动发生。

final可以关闭动态绑定,但是性能不需要我们考虑,不要试图用final来提高系统的性能。

[color=blue]缺陷:“覆盖”私有方法[/color]

由于private方法被自动认为是final方法,而且对导出类是屏蔽的。因此,在这种情况下,Derived类中的f()方法就是一个全新的方法;既然基类中的private方法在子类中式不可见的,所以也就不会被重载。

[color=blue]缺陷:域与静态方法[/color]

当sub对象转型为Super引用时,任何域访问操作都将由编译器解析,因此不是多态的。[color=red]Sub.field Super.field各自调用各自的[/color]。

如果某个方法是静态的,它的行为就不具有多态性。[color=red]静态方法是与类,而并非与单个的对象相关联的。[/color]

构造器和多态

通常,构造器不同于其他种类的方法。涉及到多态时仍是如此。尽管构造器并不具有多态性([color=red]它实际上是static方法,只不过该static声明是隐式的[/color]),但还是非常有必要理解构造器怎么通过多态在复杂的层次结构中运作。

[color=blue]构造器的调用顺序[/color]

1)调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,直到最底层的导出类。
2)按声明顺序调用成员的初始化。
3)调用导出类构造器的主体。

最后子类中的对象,即组合来的对象引用,只要有可能就应该对他们进行初始化。但遗憾的是这种做法并不适用于所有情况。

[color=blue]构造器内部的多态方法的行为[/color]
Calss Glyph {
Void draw() {
Print(“Glyph.draw()”);
}
Glyph() {
Print(“Glyph() before draw()”);
Draw();
Print(“Glyph() after draw()”);
}
}

Class RoundGlyph extends Glyph {
Private int radies = 1;
RoundGlyph(int r) {
Radies = r;
Print(“RoundGlyph.RoundGlyph(), radies = ” + radies);
}
Void draw() {
Print(“RoundGlyph.draw(), radius = ” + radius);
}
}

Public class test {
Public static void main(String[] agers) {
New RoundGlyph(5);
}
}

结果:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph() radies = 5


看上面的代码,我们会发现父类构造方法调用到的竟然是子类的draw方法。

初始化的实际过程:
1) 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
2) 如前所述那样调用基类构造器。此时调用被覆盖后的draw()方法,由于1的缘故此时的radius的值是0。
3) 按照声明的顺序调用成员的初始化方法。
4) 调用导出类的构造器主体。

[color=red]就是当调用到父类的构造器的时候,子类的成员变量还没有被初始化,但是方法什么的都已经加载完毕了,所以调用的是子类的方法,但是子类的成员变量没有初始化,所以为0.
[/color]
因此,编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的画,避免调用其他方法”。在构造器内唯一能够安全调用的那些方法是基类中的final方法。这些方法不能被覆盖,因此也就不会出现上述令人惊讶的问题。你可能无法总是能够遵循这条准则,但是应该朝着它努力。

[color=blue]纯继承与扩展[/color]

导出类中接口的扩展部分不能被基类访问,因此,一旦我们向上转型,就不能调用那些新方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值