分享
 
 
 

java学习笔记7--Polymorphism

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

无标题文档

Polymorphism

多态(polymorphism)也被称为动态绑定『dynamic binding』、后绑定『late binding』或运行时绑定『run-time bingding』。

它的意思是要在程序运行的时候,根据对象的类型来决定该绑定哪个方法。多态性是继数据抽象和继承之后的,面向对象的编程语言的第三个基本特性。

绑定(binding): 将方法的调用连到方法本身

前绑定(early binding): 当绑定发生在程序运行之前时(就是由编译器或连接器负责)

后绑定(late binding): 在程序运行的时候,根据对象的类型来决定该绑定哪个方法。

“封装”(encapsulation)通过将数据的特征(characteristics)与行为(behavior)结合在一起,创建了一种新的数据类型。

“隐藏实现”(Implementation hiding)通过将细节设成private,完成了接口与实现的分离。

“多态性”是站在“class”的角度来处理这种逻辑上的分离的。

Shape s = new Circle(); //upcasting: 将Circle对象upcast为Shape类型

s.draw();

Upcast(上传)就是把对象的reference 当作基类的reference 来用.(注:java内部是知道对象属于什么类型的)

因为Derived class is a type of base class, 所以基类的reference (Shape s)能接受派生类(circle)的对象

upcast以后,基类reference 调用的都是基类自己的method (late binding),

除非这个method是late-bound的,也就是派生类覆写(override)了这个method, 才会根据对象类型选择相应的method(多态性).

以上面的代码为例:s是Shape类型的reference, 除非draw()是一个动态绑定的method(派生类circle覆写了这个draw()),

s.draw()才会调用cicle的draw(), 否则调用的都是基类Shape自己的method

private和final的method都会采用early-binding, 因为他们是不能被override的。(注:private 方法自动就是final 的)

建议别用基类的private method的名字去命名派生类的method。因为这样会让人误以为会override这个method,

实际上private自动就是final的,不能被override。

override(覆写) 表示在派生类里写一个"同样"的method。 就是重新写一遍这个method

(注:1."同样"表示:同名且参数列表和返回值也相同。 2.method必须是非final, 非private的(private 方法自动就是final 的) )

overload:除了"同样"的method(即override) 以外的同名method.

当你想要通过一个公共的接口来操控一组类的时候,就可以使用抽象类了。通过动态绑定机制,那些符合方法特征的派生类方法将会得到调用。

abstract class {}

抽象类的方法可以使abstract的,也可以是非abstract。派生类继承了一个abstract类,那他要么实现所有的abstract的method,要么把自己也变成abstract的。

//: c07:PolyConstructors.java

// Constructors and polymorphism

// don't produce what you might expect.

import com.bruceeckel.simpletest.*;

abstract class Glyph {

abstract void draw();

Glyph() {

System.out.println("Glyph() before draw()");

draw();//如果你new一个派生类对象,并且在基类构造函数里面调用了动态绑定的方法,

//那么它会使用那个派生类覆写后的版本。

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 {

private static Test monitor = new Test();

public static void main(String[] args) {

new RoundGlyph(5);

monitor.expect(new String[] {

"Glyph() before draw()",

"RoundGlyph.draw(), radius = 0",

"Glyph() after draw()",

"RoundGlyph.RoundGlyph(), radius = 5"

});

}

} ///:~

如果你new一个派生类对象,并且在基类构造函数里面调用了动态绑定的方法,那么它会使用那个派生类覆写后的版本。

真正的初始化过程是这样的:

1. 在进行其它工作之前,分配给这个对象的内存会先被初始化为两进制的零。

2. 正如前面一直在所说的,先调用基类的构造函数。这时会调用被覆写的draw( )方法(是的,在调用RoundGlyph 的构造函数之前调用),

这时它发现,由于受第一步的影响,radius 的值还是零。

3. 数据成员按照它们声明的顺序进行初始化。

4. 调用派生类的构造函数的正文。

一个好的构造函数应该: 用最少的工作量把对象的状态(fields)设置好,而且要尽可能地避免去调用方法。

构造函数唯一能安全调用的方法,就是基类的final/private 方法。它们不会被覆写,因此也不会产生这种意外的行为。

一旦理解了多态性,你就会觉得所有东西应该都是继承下来的,因为多态性实在是太聪明了。但是这样做会加重设计的负担。

实际上,如果你一遇到“要用已有的类来创建新类”的情况就想到要用继承的话,事情就会毫无必要地变得复杂起来了。

较好的办法还是先考虑合成,特别是当你不知道该继承哪个类的时候。合成并不强求你把设计搞成一个类系。

此外它还更灵活,因为使用合成的时候,你可以动态地选择成员的类型(以及它们的行为),而使用继承的话,

就得在编译时指明对象的确切类型。下面这段程序就演示了这一点:

//: c07:Transmogrify.java

// Dynamically changing the behavior of an object

// via composition (the "State" design pattern).

import com.bruceeckel.simpletest.*;

abstract class Actor {

public abstract void act();

}

class HappyActor extends Actor {

public void act() {

System.out.println("HappyActor");

}

}

class SadActor extends Actor {

public void act() {

System.out.println("SadActor");

}

}

class Stage {

private Actor actor = new HappyActor();

public void change() { actor = new SadActor(); }

public void performPlay() { actor.act(); }

}

public class Transmogrify {

private static Test monitor = new Test();

public static void main(String[] args) {

Stage stage = new Stage();

stage.performPlay();

stage.change();

stage.performPlay();

monitor.expect(new String[] {

"HappyActor",

"SadActor"

});

}

} ///:~

有一条一般准则“使用继承来表示行为的不同,而用成员数据来表示状态的不同”。上述例程同时体现这两者;两个派生类用来表示act()方法的不同,

而Stage则使用合成来表示状态的变化。在这种情况下,状态的不同会导致行为的不同。

下传(Downcast):把基类重新转回具体的派生类类型,使得reference可以调用派生类的extended接口

Java 类型传递都要经过检查!所以,尽管看上去只是用一对括号作了些普通的的类型转换,但是运行的时候,系统会对这些转换作检查,

以确保

它确实是你想要转换的类型。如果不是,你就会得到一个ClassCastException。这种运行时的类型检查被称为“运行时的类型

鉴别(run-time type identification 缩写为RTTI)”。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有