c java多态_浅谈Java多态

52d0d81eddc7593705e70a5282777f1e.png

什么是Java中的多态?又是一个纸老虎的概念,老套路,把它具体化,细分化,先想三个问题(注意,这里不是简单的化整为零,而是要建立在学习一个新概念时的思考框架):

1.这个东西有什么用?用来干什么的?它的意义在哪里?(显然,如果是没用的东西,就没必要浪费时间了;其实,弄懂了这个问题,就掌握了50%)

2.这个概念或者技能点怎么用?也就是它的表现形式,如关键字、修饰词、语法什么的。。。(这个占25%)

3.这个东西在用的过程中,有哪些关键点和细节点?(是的,也占25%)

上面三个问题搞清楚了,剩下的就是去用了。。。“无他,但手熟尔。”

一、第一个问题:多态有什么用?它存在的意义是什么?

多态的作用:使代码具有松耦合性,满足开闭原则。(多态,意味着一个对象有着多重特征,可以在特定的情况下,表现不同的状态,从而对应着不同的属性和方法。)WTF?又装x!松耦合是什么鬼?还开闭原则?能不能直给?好吧,所谓的“耦合”就是,当你扩展功能的时候,其他与之相应的源代码也要修改,有点像麻花一样纠缠不清;“松耦合”是指你虽然扩展了,但不需要修改其他的代码。这样就达到了对扩展开放,对修改关闭的效果。

具体怎样,看个例子就懂了:小强,一个农村热血青年,来到大城市打工,每天披星戴月,辛勤工作,终于存了些钱。于是小强带着钱回到老家,请村东头的媒婆王大娘给自己介绍对象。小强在焦急等待几天之后,看到王大娘笑眯眯走过来,“强啊,大娘给你找到姑娘了”。。。

故事先讲到这儿,我们用代码来把这件事实现一下:

1 public class Girl{ //父类--姑娘

2 public int faceScore=60; //颜值

3 public int love=0;4 public void say(){ //自我介绍

5 System.out.println("hello world!");6 }7 public void addLove(){ //被求爱后,增加爱心值

8 }9 }10 public class HubeiGirl extends Girl{ //子类--湖北姑娘

11 public int faceScore=70;12 public voidsay(){13 System.out.println("我叫小红,我很聪明,也很会做饭。我的爱心值:"+love);14 }15 public void addLove() { //重写增加爱心值方法

16 love+=20;17 System.out.println("我的爱心值是:"+love);18 }19 public void cook(){ //特有方法--做饭

20 System.out.println("红丸子,炸丸子,四喜丸子。。。");21 }22 }23 public class HunanGirl extends Girl{ //子类--湖南姑娘

24 public int faceScore=85;25 public voidsay(){26 System.out.println("我叫小倩,我很可爱,也很会唱歌。我的爱心值:"+love);27 }28 public void addLove() { //重写增加爱心值方法

29 love+=10;30 System.out.println("我的爱心值是:"+love);31 }32 public void sing(){ //特有方法--唱歌

33 System.out.println("辣妹子辣~辣妹子辣~~");34 }35 }

代码很简单,一个姑娘父类,两个子类分别是湖北姑娘和湖南姑娘,好,现在小强见了两个姑娘想向她们分别表达爱意(被求爱后,爱心值会增加),该怎么实现?代码如下:

1 public class XiaoQiang{ //小强类

2 public void courting(HubeiGirl b){ //向湖北姑娘表达爱意

3 b.addLove();4 }5 public void courting(HunanGirl n){ //向湖南姑娘表达爱意

6 n.addLove();7 }8 }9 public classTest{10 public static voidmain(String[] argrs){11 HubeiGirl xiaohong = newHubeiGirl();12 xiaohong.say();13 HunanGirl xiaoqian = newHunanGirl();14 xiaoqian.say();15 XiaoQiang qiang = newXiaoQiang();16 qiang.courting(xiaohong); //小强向湖北姑娘小红表达爱意

17 qiang.courting(xiaoqian); //小强向湖南姑娘小倩表达爱意

18 }19 }

这里先定义了一个小强类,里面有两个表达爱意的方法,参数是不同的对象类型,属于重载。测试类中创建小红、小倩和小强对象,运行结果如下:

477036c5d6929508de67c25d127cc763.png

由结果可以看到,传入不同的对象类型参数,小强调用不同的表达爱意的方法courting(),要求满足,bingo!故事继续,王大娘的婚介事业已经走出中国,在向国际化发展,王大娘认识一个非洲姑娘玛丽卡(如上图),要把玛丽卡介绍给小强。。。这要怎么实现?如果照着上面来:

1 public class AfricaGirl extends Girl{ //子类--非洲姑娘

2 public int faceScore=80;3 public voidsay(){4 System.out.println("我叫玛丽卡,我很热情,也很会跳舞。我的爱心值:"+love);5 }6 public void addLove() { //重写增加爱心值方法

7 love+=15;8 System.out.println("我的爱心值是:"+love);9 }10 public void dance(){ //特有方法--跳舞

11 System.out.println("动起来!gogogogo for it!动起来!");12 }13 }14 public class XiaoQiang{ //小强类

15 public void courting(HubeiGirl b){ //向湖北姑娘表达爱意

16 b.addLove();17 }18 public void courting(HunanGirl n){ //向湖南姑娘表达爱意

19 n.addLove();20 }21 public void courting(AfricaGirl a){ //向非洲姑娘表达爱意

22 a.addLove();23 }24 }

这种做法是先定义子类非洲姑娘,这是必须的,在小强类里加了一个courting(AfricaGirl a)方法,这样也能实现要求,但是,这样有两个问题:1.增加非洲姑娘类的时候,改动了小强类,不是松耦合,不满足开闭原则;2.假如王大娘的业务发展的很好,要给小强介绍100个姑娘,难道要改变100次小强类,往里面加100个方法吗?

所以,问题来了:要怎样定义小强类,使得即使不断增加姑娘也不用改动小强类?这里就要用到多态了,也是开篇第二个问题的答案。

二、第二个问题:多态怎么用?

其实,就一句话:父类类型的引用指向子类的对象。用多态的思想来定义上面的小强类,如下:

1 public class XiaoQiang{ //小强类

2 public void courting(Girl g){ //参数为父类类型

3 g.addLove();4 }5 }

很神奇,可以看到用多态的思想来做,只需要一个参数为父类Girl类型的方法就够了,先来测试一下:

1 public classTest {2 public static voidmain(String[] args) {3 HubeiGirl xiaohong = newHubeiGirl();4 xiaohong.say();5 HunanGirl xiaoqian = newHunanGirl();6 xiaoqian.say();7 AfricaGirl malika = newAfricaGirl();8 malika.say();9 XiaoQiang qiang = newXiaoQiang();10 qiang.courting(xiaohong); //小强向湖北姑娘小红表达爱意

11 qiang.courting(xiaoqian); //小强向湖南姑娘小倩表达爱意

12 qiang.courting(malika); //小强向非洲姑娘玛丽卡表达爱意

13

14 }15 }

6fe2535bd22cec1d2c01df821de1131e.png

功能实现,而且现在无论王大娘给小强介绍多少姑娘都不用修改小强类了。那么,为什么把参数从子类类型变成父类类型就能达到如此效果,就是多态呢?

这里我们先来仔细看一下使用父类作为方法形参实现多态的过程,以“小强向非洲姑娘玛丽卡表达爱意”为例,qiang.courting(malika)执行的时候,由于对象作为参数时,传入的只是对象的引用,因此参数部分发生的是:Girl g = malika,也就是将父类类型Girl的引用g指向了子类对象malika(有的说法是,将子类类型的指针赋值给父类类型的指针,一个意思,一般指针是C和C++里的概念),是的,这就是Java机制允许的父类类型的引用指向子类的对象,就是多态。调用过程是:当我们用一个父类型引用指向子类对象时,会先访问子类中重写的父类方法(父类的方法不会再执行),如果子类没有重写父类的方法,才会执行父类中的方法。而这种调用方式,就是多态的一种状态,叫做向上转型,也是最为容易理解的一种多态方式。具体到上面的例子是,先访问子类AfricaGirl中的重写的addLove()方法,有,就不会再去访问父类Girl中的addLove()方法了。

好,故事继续,老人家讲过:凡是不以结婚为目的的谈恋爱都是耍流氓。小强是正经人,于是他要选一个最中意的姑娘,并把这个姑娘的对象返回出来,好让王大娘进行其它操作。。。这个需求怎么实现?

1 public class XiaoQiang{ //小强类

2 public void courting(Girl g){ //参数为父类类型

3 g.addLove();4 }5

6 public Girl chooseGirl(int num) { //选择姑娘的方法,返回值为Girl类型

7 switch(num) {8 case 1:9 HubeiGirl b = new HubeiGirl(); //num为1时,返回湖北姑娘的对象b

10 returnb;11 case 2:12 HunanGirl n = newHunanGirl();13 returnn;14 case 3:15 AfricaGirl a = newAfricaGirl();16 returna;17 default:18 break;19 }20 return null;21 }22 }

可以看到,小强类中增加了一个选择姑娘的方法,根据不同的数字返回不同的子类姑娘对象,重点是它的返回值是父类Girl类型,这就是使用父类作为返回值实现多态。与上面使用父类作为方法形参实现多态相对,这里是在返回值的时候将父类类型的引用指向子类对象。在测试类Test中调用一下:

1 public classTest {2 public static voidmain(String[] args) {3 XiaoQiang qiang = newXiaoQiang();4 Girl g = qiang.chooseGirl(1); //返回值是Girl类型,将返回值赋给Girl类型的引用g

5 qiang.courting(g); //表达爱意,增加爱心值

6 g.say();7 }8 }

93ff9a99ec698021d4507e42c5e30cc7.png

可以看到,传入数字1,返回的是湖北姑娘小红,既然小红很会做饭,那我们就调用一下她做饭的方法cook(),如下:

03b6ee65de3393861fddc7b91cb283dc.png

f0ca4574f2153e453836d0a10ee9dff0.png

看样子不太妙,报错说cook()方法没有定义,有人可能会问子类HubeiGirl中不是有cook()方法吗,问题是现在的g是父类Girl类型的引用,而父类Girl中是没有cook()方法的。这就涉及到向上转型的一个特点:向上转型后,父类类型的引用只能调用子类中的重写的父类方法和父类中的方法,而不能调用子类中特有的方法。这里cook()是子类HubeiGirl特有的方法,所以不能调用。

娶个老婆居然不会做饭,小强肯定不高兴,那该怎么办?这里就需要将返回的父类型返回值强转为子类型,并将其赋值给相应子类型的引用。如下:

78b3f550c69946cb054964d25a06917b.png

1b8e7df87f8c4fd10a364134adb6a668.png

可以看到,调用cook()成功!如图所示,将父类Girl类型的返回值强转成子类HubeiGirl类型,再将其赋给子类HubeiGirl类型的引用g,就可以调用子类HubeiGirl中特有的方法了。这种将子类型对象向上转型成父类型后,再将其转回子类型的过程就是所谓的向下转型。(注:不能直接将父类型对象转型成子类型,一定要之前有向上转型这个过程才行。)

好,故事讲完,小强和小红从此过上了幸福的生活。

三、第三个问题:使用多态的关键点和细节?

这个问题在二中已经回答得差不多了,总结一下:

1.继承:多态是发生在有继承关系的子类和父类中的。

2.重写:多态就是多种形态。也就是说,当我们需要实现多态的时候,就需要有父类的方法被子类重写。否则,如果没有重写的方法,就看不出多态的特性,一切按照父类的方法来,还不如不要继承,直接在父类中添加相应的方法,然后在实例化好了。

3.向上转型:父类类型的引用指向子类的对象。

4.向下转型:将子类型对象向上转型成父类型后,再将其转回子类型的过程。

5.在Java中有两种形式可以实现多态,继承和接口。原理相同,本文只讲了继承情况。

四、经典例题

既然学了东西就得用,不然那学东西有什么用。下面是一道关于多态的经典例题,可以试一下不看答案能不能做出来:

1 public classA {2 publicString show(D obj) {3 return ("A and D");4 }5 publicString show(A obj) {6 return ("A and A");7 }8 }9

10 public classB extends A{11 publicString show(B obj){12 return ("B and B");13 }14 publicString show(A obj){15 return ("B and A");16 }17 }18

19 public classC extends B{20

21 }22 public classD extends B{23

24 }25

26 public classTest {27 public static voidmain(String[] args) {28 A a1 = newA();29 A a2 = newB();30 B b = newB();31 C c = newC();32 D d = newD();33

34 System.out.println("1--" +a1.show(b));35 System.out.println("2--" +a1.show(c));36 System.out.println("3--" +a1.show(d));37 System.out.println("4--" +a2.show(b));38 System.out.println("5--" +a2.show(c));39 System.out.println("6--" +a2.show(d));40 System.out.println("7--" +b.show(b));41 System.out.println("8--" +b.show(c));42 System.out.println("9--" +b.show(d));43 }44 }

运行结果如下:

1--A and A2--A and A3--A and D4--B and A5--B and A6--A and D7--B and B8--B and B9--A and D

不知道大家做对了几个,有没有像福尔摩斯推理探案一样的感觉,现在就让我们戴上猎鹿帽,叼上烟斗,拿着放大镜开始吧,首先看看下面这张表示ABCD四个类关系的图片:

04b3f121914f50ed7ce954ab13eaf421.png

由上图可以看到:

1.B类中的show(A obj)是继承并重写了A类中的show(A obj)方法;

2.B类中的show(B obj)是重载,B相对A特有的方法;

3.B继承了A的方法,C和D继承了B的方法(包含了A的方法)。

现在来一个一个看:

1---- a1.show(b)

子类对象b作为参数,而a1只能调用show(D obj)和show(A obj)两个方法,显然调用show(A obj),典型的多态应用。

2----a1.show(c)

子类的子类对象c作为参数,同样的,a1只能show(D obj)和show(A obj)两个方法,显然调用show(A obj),这里表现的是多态中,子类对象不光可以赋给父类的引用,父类以上的引用都可以。而Java里最终极的父类是Object,所以,Object的引用可以指向任何对象。

3----a1.show(d)

子类的子类对象d作为参数,同样的,a1只能show(D obj)和show(A obj)两个方法,显然。。。额,里面有D类型作为参数的方法,所以显然调用show(D obj)。

4----a2.show(b)

A a2 = new B(); 其中,a2是父类型的引用指向了子类B类型的对象,多态的应用,所以a2能调用的方法有:A类中的show(D obj)、B类中的show(A obj)两个方法。为什么不能调用A类中的show(A obj)方法?因为子类B已经继承并重写了父类A类中的show(A obj), 所以只会访问子类B中的show(A obj),不会再去访问父类A中的show(A obj)。为什么不能调用B类中的show(B obj)方法?因为此方法是子类B特有的方法,多态中父类A类型的引用a2不能访问。现在是a2.show(b),参数是子类对象b,显然调用B类中的show(A obj)方法。

5----a2.show(c)

同上,a2能调用的方法有:A类中的show(D obj)、B类中的show(A obj)两个方法。B的子类对象c作为实参,显然调用B类中的show(A obj)方法,类C的父类的父类型作为形参实现多态。

6---a2.show(d)

同上,a2能调用的方法有:A类中的show(D obj)、B类中的show(A obj)两个方法。B的子类对象d作为实参,里面有D类型作为形参的方法,所以显然调用A类中的show(D obj)方法。

7----b.show(b)

B b = new B();这个就是调用B类中的show(B obj)。

8----b.show(c)-------------------它仍然要按照继承链中调用方法的优先级来确认。

B b = new B();其中,B类继承了A类的方法,b可以调用的方法有:A类中的show(D obj)、B类中的show(A obj)、B类中的show(B obj)三个方法。B的子类对象c作为实参,调用父类B中的show(B obj),使用父类作为形参实现多态。为什么不能调用B类中的show(A obj)方法?类A是类C父类的父类,将A类型的引用指向类C的对象c不也是多态允许的?理论上是的,但实际情况是,现在有父类作为形参和父类的父类作为形参实现多态这两种选择。这时它就要按照按照继承链中调用方法的优先级来确认,父类B比父类的父类A优先。其中优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

9----b.show(d)

同上,b可以调用的方法有:A类中的show(D obj)、B类中的show(A obj)、B类中的show(B obj)三个方法。。。额,里面有D类型作为参数的方法,所以显然调用A类中的show(D obj)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值