多态,大概每个人都知道。但是,又有几个人真的理解什么是多态、多态有哪些细节呢?如果你看到这篇文章的名字,脑海中对多态没有一个清晰的概念,不妨点进来看看,也许会有收获。

什么是多态

简单的理解多态

多态,简而言之就是同一个行为具有多个不同表现形式或形态的能力。比如说,有一杯水,我不知道它是温的、冰的还是烫的,但是我一摸我就知道了。我摸水杯这个动作,对于不同温度的水,就会得到不同的结果。这就是多态。

那么,java中是怎么体现多态呢?我们来直接看代码:

public class Water {public void showTem(){System.out.println("我的温度是: 0度");}
}public class IceWater extends Water {public void showTem(){System.out.println("我的温度是: 0度");}
}public class WarmWater extends Water {public void showTem(){System.out.println("我的温度是: 40度");}
}public class HotWater extends Water {public void showTem(){System.out.println("我的温度是: 100度");}
}public class TestWater{public static void main(String[] args) {Water w = new WarmWater();w.showTem();w = new IceWater();w.showTem();w = new HotWater();w.showTem();}
}//结果:
//我的温度是: 40度
//我的温度是: 0度
//我的温度是: 100度

这里的方法showTem()就相当于你去摸水杯。我们定义的water类型的引用变量w就相当于水杯,你在水杯里放了什么温度的水,那么我摸出来的感觉就是什么。就像代码中的那样,放置不同温度的水,得到的温度也就不同,但水杯是同一个。

想必你也看出来了,这段代码中最关键的就是这一句

Water w = new WarmWater();

看过我前几篇文章的应该知道,我说在讲多态的时候,会讲一个很重要的知识点 — 向上转型。

这句代码体现的就是向上转型。后面我会详细讲解这一知识点。

多态的分类

已经简单的认识了多态了,那么我们来看一下多态的分类。

多态一般分为两种:重写式多态和重载式多态。重写和重载这两个知识点前面的文章已经详细将结果了,这里就不多说了。

  • 重载式多态,也叫编译时多态。也就是说这种多态再编译时已经确定好了。重载大家都知道,方法名相同而参数列表不同的一组方法就是重载。在调用这种重载的方法时,通过传入不同的参数最后得到不同的结果。

    但是这里是有歧义的,有的人觉得不应该把重载也算作多态。因为很多人对多态的理解是:程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,这种情况叫做多态。 这个定义中描述的就是我们的第二种多态—重写式多态。并且,重载式多态并不是面向对象编程特有的,而多态却是面向对象三大特性之一(如果我说的不对,记得告诉我。。)。

    我觉得大家也没有必要在定义上去深究这些,我的理解是:同一个行为具有多个不同表现形式或形态的能力就是多态,所以我认为重载也是一种多态,如果你不同意这种观点,我也接受。

  • 重写式多态,也叫运行时多态。这种多态通过动态绑定(dynamic binding)技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。
    这种多态通过函数的重写以及向上转型来实现,我们上面代码中的例子就是一个完整的重写式多态。我们接下来讲的所有多态都是重写式多态,因为它才是面向对象编程中真正的多态。

    动态绑定技术涉及到jvm,暂时不讲(因为我也不懂,哈哈哈哈),感兴趣的可以自己去研究一下,我暂时还没有时间去研究jvm。。

多态的条件

前面说过,我们接下来说的多态,都是运行时多态。

  • 继承。在多态中必须存在有继承关系的子类和父类。
  • 重写。子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
  • 向上转型。在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

继承也可以替换为实现接口。

继承和重写之前都说过了,接下来我们来看一下转型是什么。

向上转型与向下转型

向上转型

子类引用的对象转换为父类类型称为向上转型。通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口。

案例驱动

看一个大家都知道的例子:

public class Animal {public void eat(){System.out.println("animal eatting...");}
}public class Cat extends Animal{public void eat(){System.out.println("我吃鱼");}
}public class Dog extends Animal{public void eat(){System.out.println("我吃骨头");}public void run(){System.out.println("我会跑");}
}public class Main {public static void main(String[] args) {Animal animal = new Cat(); //向上转型animal.eat();animal = new Dog();animal.eat();}}//结果:
//我吃鱼
//我吃骨头

这就是向上转型,Animal animal = new Cat();将子类对象Cat转化为父类对象Animal。这个时候animal这个引用调用的方法是子类方法。

关于方法调用的顺序,我们后面会详细讲解。

转型过程中需要注意的问题

  • 向上转型时,子类单独定义的方法会丢失。比如上面Dog类中定义的run方法,当animal引用指向Dog类实例时是访问不到run方法的,animal.run()会报错。
  • 子类引用不能指向父类对象。Cat c = (Cat)new Animal()这样是不行的。

向上转型的好处

  • 减少重复代码,使代码变得简洁。
  • 提高系统扩展性。

举个例子:比如我现在有很多种类的动物,要喂它们吃东西。如果不用向上转型,那我需要这样写:

public void eat(Cat c){c.eat();
}public void eat(Dog d){d.eat();
}
//......eat(new Cat());
eat(new Cat());
eat(new Dog());
//......

一种动物写一个方法,如果我有一万种动物,我就要写一万个方法,写完大概猴年马月都过了好几个了吧。好吧,你很厉害,你耐着性子写完了,以为可以放松一会了,突然又来了一种新的动物,你是不是又要单独为它写一个eat方法?开心了么?

那如果我使用向上转型呢?我只需要这样写:

public void eat(Animal a){a.eat();
}eat(new Cat());
eat(new Cat());
eat(new Dog());
//.....

恩,搞定了。代码是不是简洁了许多?而且这个时候,如果我又有一种新的动物加进来,我只需要实现它自己的类,让他继承Animal就可以了,而不需要为它单独写一个eat方法。是不是提高了扩展性?

向下转型

与向上转型相对应的就是向下转型了。向下转型是把父类对象转为子类对象。(请注意!这里是有坑的。)

案例驱动

先看一个例子:

//还是上面的animal和cat dog
Animal a = new Cat();
Cat c = ((Cat) a);
c.eat();
//输出  我吃鱼
Dog d = ((Dog) a);
d.eat();
// 报错 : java.lang.ClassCastException:com.chengfan.animal.Cat cannot be cast to com.chengfan.animal.Dog
Animal a1 = new Animal();
Cat c1 = ((Cat) a1);
c1.eat();
// 报错 : java.lang.ClassCastException:com.chengfan.animal.Animal cannot be cast to com.chengfan.animal.Cat

为什么第一段代码不报错呢?相比你也知道了,因为a本身就是Cat对象,所以它理所当然的可以向下转型为Cat,也理所当然的不能转为Dog,你见过一条狗突然就变成一只猫这种操蛋现象?

而a1为Animal对象,它也不能被向下转型为任何子类对象。比如你去考古,发现了一个新生物,知道它是一种动物,但是你不能直接说,啊,它是猫,或者说它是狗。

向下转型注意事项

  • 向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
  • 向下转型只能转型为本类对象(猫是不能变成狗的)。

    大概你会说,我特么有病啊,我先向上转型再向下转型??

    我们回到上面的问题:喂动物吃饭,吃了饭做点什么呢?不同的动物肯定做不同的事,怎么做呢?

public void eat(Animal a){if(a instanceof Dog){  Dog d = (Dog)a;d.eat();d.run();//狗有一个跑的方法      } if(a instanceof Cat){  Cat c = (Cat)a;c.eat();System.out.println("我也想跑,但是不会"); //猫会抱怨    } a.eat();//其他动物只会吃
}eat(new Cat());
eat(new Cat());
eat(new Dog());
//.....

现在,你懂了么?这就是向下转型的简单应用,可能举得例子不恰当,但是也可以说明一些问题。

敲黑板,划重点!看到那个instanceof了么?

经典案例分析多态

基本的多态和转型我们都会了,最后加点餐。看一个经典案例:

class A {public String show(D obj) {return ("A and D");}public String show(A obj) {return ("A and A");}}class B extends A{public String show(B obj){return ("B and B");}public String show(A obj){return ("B and A");}
}class C extends B{}class D extends B{}public class Demo {public static void main(String[] args) {A a1 = new A();A a2 = new B();B b = new B();C c = new C();D d = new D();System.out.println("1--" + a1.show(b));System.out.println("2--" + a1.show(c));System.out.println("3--" + a1.show(d));System.out.println("4--" + a2.show(b));System.out.println("5--" + a2.show(c));System.out.println("6--" + a2.show(d));System.out.println("7--" + b.show(b));System.out.println("8--" + b.show(c));System.out.println("9--" + b.show(d));}
}
//结果:
//1--A and A
//2--A and A
//3--A and D
//4--B and A
//5--B and A
//6--A and D
//7--B and B
//8--B and B
//9--A and D//能看懂这个结果么?先自分析一下。

前三个,强行分析,还能看得懂。但是第四个,大概你就傻了吧。为什么不是b and b呢?

这里就要学点新东西了。

当父类对象引用变量引用子类对象时,被引用对象的类型决定了调用谁的成员方法,引用变量类型决定可调用的方法。如果子类中没有覆盖该方法,那么会去父类中寻找。

可能读起来比较拗口,我们先来看一个简单的例子:

class X {public void show(Y y){System.out.println("x and y");}public void show(){System.out.println("only x");}
}class Y extends X {public void show(Y y){System.out.println("y and y");}public void show(int i){}
}class main{public static void main(String[] args) {X x = new Y();x.show(new Y());x.show();}
}
//结果
//y and y
//only x

Y继承了X,覆盖了X中的show(Y y)方法,但是没有覆盖show()方法。

这个时候,引用类型为X的x指向的对象为Y,这个时候,调用的方法由Y决定,会先从Y中寻找。执行x.show(new Y());,该方法在Y中定义了,所以执行的是Y里面的方法;

但是执行x.show();的时候,有的人会说,Y中没有这个方法啊?它好像是去父类中找该方法了,因为调用了X中的方法。

事实上,Y类中是有show()方法的,这个方法继承自X,只不过没有覆盖该方法,所以没有在Y中明确写出来而已,看起来像是调用了X中的方法,实际上调用的还是Y中的。

这个时候再看上面那句难理解的话就不难理解了吧。X是引用变量类型,它决定哪些方法可以调用;show()和show(Y y)可以调用,而show(int i)不可以调用。Y是被引用对象的类型,它决定了调用谁的方法:调用y的方法。

上面的是一个简单的知识,它还不足以让我们理解那个复杂的例子。我们再来看这样一个知识:

继承链中对象方法的调用的优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

如果你能理解这个调用关系,那么多态你就掌握了。我们回到那个复杂的例子:

abcd的关系是这样的:C/D —> B —> A

我们先来分析4 : a2.show(b)

  • 首先,a2是类型为A的引用类型,它指向类型为B的对象。A确定可调用的方法:show(D obj)和show(A obj)。
  • a2.show(b) ==> this.show(b),这里this指的是B。
  • 然后.在B类中找show(B obj),找到了,可惜没用,因为show(B obj)方法不在可调用范围内,this.show(O)失败,进入下一级别:super.show(O),super指的是A。
  • 在A 中寻找show(B obj),失败,因为没用定义这个方法。进入第三级别:this.show((super)O),this指的是B。
  • 在B中找show((A)O),找到了:show(A obj),选择调用该方法。
  • 输出:B and A

如果你能看懂这个过程,并且能分析出其他的情况,那你就真的掌握了。

我们再来看一下9:b.show(d)

  • 首先,b为类型为B的引用对象,指向类型为B的对象。没有涉及向上转型,只会调用本类中的方法。
  • 在B中寻找show(D obj),方法。现在你不会说没找到了吧?找到了,直接调用该方法。
  • 输出 A and D。

总结

本篇文章的内容大体上就是这些了。我们来总结一下。

  1. 多态,简而言之就是同一个行为具有多个不同表现形式或形态的能力。
  2. 多态的分类:运行时多态和编译时多态。
  3. 运行时多态的前提:继承(实现),重写,向上转型
  4. 向上转型与向下转型。
  5. 继承链中对象方法的调用的优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

事实上,当你使用向上转型的时候,你就使用了多态。关于多态还有一些其他的知识点,比如几个关键字:static、final等,我们后面会一一讲解。下一篇《重新认识java(六) —-java中的另类:static关键字》不定期更新。

如果文章内容有什么问题,或者哪里出错,请及时与我联系。不保证文章内容的完全正确性。如果你有更好的简洁或者更好的理解方式,欢迎一起交流。

原文地址:http://blog.csdn.net/qq_31655965/article/details/54746235
欢迎转载~转载请注明出处!
看完了随手点个赞呗~

重新认识java(五) ---- 面向对象之多态(向上转型与向下转型)相关推荐

  1. Java 面向对象编程(三)——多态,方法重写,向上转型,向下转型

    所谓多态,就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪 ...

  2. 【Java挠头】继承、抽象、接口、多态、向上转型、向下转型等精妙干货

    –本文摘要–     1.本文的目标读者:是了解Java基础,且对面向对象概念有所了解,但是对Java中继承.抽象类.接口.多态等等概念搞不清的初学者.     2.本文的内容摘要:讲述Java中的一 ...

  3. 第三次学JAVA再学不好就吃翔(part35)--多态向上转型和向下转型

    学习笔记,仅供参考 文章目录 面向对象 多态 多态向上转型和向下转型 多态的好处和弊端 面向对象 多态 多态向上转型和向下转型 我们首先通过以下代码复习一下自动类型提升和强制类型转换: class B ...

  4. 【转】面向对象之多态(向上转型与向下转型)

    转:https://blog.csdn.net/qq_31655965/article/details/54746235. 多态,大概每个人都知道.但是,又有几个人真的理解什么是多态.多态有哪些细节呢 ...

  5. 【JavaSE】面向对象之多态、向上转型与向下转型

    本专栏为 JavaSE 的学习笔记及相关项目,专栏长期免费更新 ❤️ ❤️ ❤️ ❤️ 个人主页:Nezuko627 欢迎来访~~~ ⭐️往期回顾: [JavaSE]继承中内存发生了什么变化?这篇文章 ...

  6. 秒懂Java多态的引用类型转换——向上转型、向下转型(强制转型)

    多态的转型分为向上转型和向下转型两种 向上转型 多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的.当父类引用指向一个子类对象时,便是向上转型. 使用格式: 父类类型 变量名 = new 子 ...

  7. 关于java多态,向上转型,向下转型

    首先,java多态是建立在java继承的基础上的,它表现的是同一种行为的不同表现形式,注意,是行为,即多态是java父子类中的方法的一个特性,而不是父子类中的属性的一个特性,比如: Person p1 ...

  8. java理解向上转型和向下转型

    在讲述向上转型和向下转型之前,我们先简单讲解下多态,如果后续看了有关多态的讲解还是不弄,只能自己上网去查阅和理解. 多态概念: 多态是面向对象程序设计(OOP)的一个重要特征,指同一个实体同时具有多种 ...

  9. 多态、向上转型、向下转型

    7.2.1 引入 多态是继封装.继承之后,面向对象的第三大特性. 生活中,比如求面积的功能,圆.矩形.三角形实现起来是不一样的.跑的动作,小猫.小狗和大象,跑起来是不一样的.再比如飞的动作,昆虫.鸟类 ...

  10. Java 转型问题(向上转型和向下转型)

    Java 转型问题其实并不复杂,只要记住一句话:父类引用指向子类对象. 什么叫父类引用指向子类对象? 从 2 个名词开始说起:向上转型(upcasting) .向下转型(downcasting). 举 ...

最新文章

  1. 多传感器融合之滤波(一)——卡尔曼滤波(KF)推导
  2. JVM调优总结(一)- 一些概念
  3. 深入入门正则表达式(java) - 匹配原理 - 1 - 引擎分类与普适原则
  4. 趣话题:同为技术岗,算法和开发哪个才是你的真命女神?
  5. 检查mysql的replication_MySQL Replication需要注意的问题
  6. 中国首富们三十而立的年纪都在干什么
  7. tar oracle home 权限不够,linux系统安装jdk
  8. CSS 设置背景颜色透明,文字不透明
  9. MegaCli查看RIAD相关信息
  10. python统计及格不及格人数_Python一次输入多个值(列出存储信息),python,一次性,数值,列表...
  11. 零基础学python-如何从零基础自学Python?
  12. Java单元测试框架 - JUnit
  13. 408计算机考研真题推荐,2017计算机408考研真题.pdf
  14. 【数据分析软件】【Weka】第一课:超easy安装教程
  15. 运动目标检测之关键帧提取、二帧差法及三帧差法
  16. 计算机研究生论文多少字,研究生一篇论文需要多少字?
  17. 使用ffmpeg合并mp4文件
  18. USDT信用卡和转账入账接口
  19. 棋盘覆盖问题(分治)
  20. 《IOG:Interactive Object Segmentation with Inside-Outside Guidance》论文笔记

热门文章

  1. Beagleboneblack的MLO文件干了些啥
  2. win2008服务器系统玩红警,Win10系统不能玩红警2的解决方法
  3. 一周热图|杨紫韩国艺匠婚纱大片出炉;易烊千玺代言麦当劳;洛天依音乐综艺节目首秀...
  4. 6v网站迅雷下载电影任务出错
  5. 揭开HPC应用的神秘面纱
  6. 【Java WEB】Linux常用命令汇总
  7. windows修改IP命令/脚本
  8. linux里关于Tab/Ctrl/Shift/Insert的快捷键
  9. 实验吧_网站综合渗透_Discuz!
  10. 你的宽带ip地址被100.64了吗?