Java 特性之多态性
多态性
面向对象(OOP)三大特性:封装、继承、多态。
多态(polymorphism)指同一行为具有多种不同表现形式,在面向对象程序设计中表现为同一消息可以根据发送对象的类型不同,做出多种不同的行为。
多态的优点
多态性能够从一定程度上消除类型之间的耦合关系,通过统一接口方式,不同类的对象可以直接替换,程序更加灵活,可扩展。
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象
多态的实现方式
重写(Override)与重载(Overload)
方法重载(Method Overloading)
方法重载(Method Overloading)允许类具有多个相同名称的方法,但是方法参数列表不同。
重载形式:
case 1: 参数数量变化(有效)
add(int, int) add(int, int, int)
case 2: 参数数据类型变化(有效)
add(int, int) add(int, float)
case 3: 参数数据类型顺序变化(有效)
add(int, float) add(float, int)
bad case 1: 仅改变返回类型(无效)
int add(int, int) float add(int, int)
Java 方法签名由方法名和其后的参数列表共同决定,仅改变返回类型编译器无法重载。
方法重载(Method Overloading)允许改变返回类型和存取权限。
方法重载(Method Overloading)式多态性,即方法调用取决于调用时传递的参数(数量、类型、顺序),属于编译时静态多态性。
方法重写(Method Overriding)
方法重写(Method Overriding)允许子类对父类可以访问的方法,实现自定义行为。重写的优点在于,无需修改父类代码即可改变子类继承的方法。
重写形式:
重写依赖继承,通过父类引用,指向子类对象实现动态多态性。
public class Animal{ public void sound(){ System.out.println("Animal is making a sound"); } } public class Cat extends Animal{ @Override public void sound(){ System.out.println("Meow"); } public static void main(String args[]){ Animal obj = new Cat(); obj.sound(); } }
输出:
Meow
重写(覆盖)规则
- 参数列表必须一样,返回类型需要兼容。
- 不能降低方法的存取权限。
- static, private, final 标记的方法以及类的构造方法不能被重写覆盖。
静态绑定与动态绑定
多态的类型可以分为运行时和编译时,方法重写(Method Overriding)代表运行时动态多态性,方法重载(Method Overloading)代表编译时静态多态性。
方法调用与方法体的关联称为绑定,有两种类型的绑定:在编译时发生的静态绑定(Static Binding or Early Binding)和在运行时发生的动态绑定(Dynamic Binding or Late Binding)。
static, private, final 标记的方法以及类的构造方法是编译时静态绑定的。因为此类方法无法覆盖,并且类的类型在编译时即可确定。其他非标记的方法可以称为“虚函数”,Java 中其实并没有“虚函数”的概念,所有普通函数(方法)默认都相当于 C++ 的”虚函数”允许覆盖(Override),因此虚函数(virtual method)能够根据运行时的具体对象进行动态绑定实现动态多态性,例如方法重写(Method Overriding)。
静态绑定示例:
class Human{ public static void walk() { System.out.println("Human walks"); } } class Boy extends Human{ public static void walk(){ System.out.println("Boy walks"); } public static void main( String args[]) { /* Reference is of Human type and object is * Boy type */ Human obj = new Boy(); /* Reference is of Human type and object is * of Human type. */ Human obj2 = new Human(); obj.walk(); obj2.walk(); } }
输出:
Human walks Human walks
声明为 static 的方法不能被重写,但是能够被再次声明。
Static Binding vs Dynamic Binding
- 静态绑定发生在编译时,而动态绑定发生在运行时。
- 静态绑定使用的是类信息:类的类型决定调用方法,而动态绑定使用的是对象信息:对象的类型决定调用方法。
- 方法重载使用静态绑定,而方法重写使用动态绑定。
综合练习
多态示例:
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 Main { 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
详细分析:
A、B、C、D 各类继承关系如图所示:
-
A a1 = new A();
正常创建对象 a1,涉及函数重载 show(),a1 具有调用方法一 show(D obj) 和方法二 show(A obj) 的能力。a1.show(b)
由编译器进行静态绑定(前期绑定)方法二 show(A obj)。 -
a1.show(c)
由编译器进行静态绑定(前期绑定)方法二 show(A obj)。 -
a1.show(d)
由编译器进行静态绑定(前期绑定)方法一 show(D obj)。 -
A a2 = new B();
多态创建父类引用,指向子类对象,a2 向上转型具有调用 A 类方法一 show(D obj) 和方法二 show(A obj) 的能力,其中子类 B 重写父类 A 的方法二 show(A obj) 为方法四 show(A obj)。记住向上转型存在缺点,即不能调用子类中有,父类没有的方法,如方法三 show(B obj)。a2.show(b)
运行时动态绑定(后期绑定)方法四 show(A obj)。 -
a2.show(c)
运行时动态绑定(后期绑定)方法四 show(A obj)。 -
a2.show(d)
由编译器进行静态绑定(前期绑定)方法一 show(D obj)。 -
B b = new B();
正常创建对象 b,涉及函数重载 show(),b 具有调用方法三 show(B obj) 和方法四 show(A obj) 的能力。同时 B 继承自 A 因此拥有方法一 show(D obj) 和方法二 show(A obj) 其中方法二被方法四重写覆盖。b.show(b)
由编译器进行静态绑定(前期绑定)方法三 show(B obj)。 -
b.show(c)
由编译器进行静态绑定(前期绑定)方法三 show(B obj)。 -
b.show(d)
由编译器进行静态绑定(前期绑定)方法一 show(D obj)。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
如何用 Python 脚本批量下载 Google 图像?
分不清谭卓和郝蕾?各来200张照片,让深度学习帮我们识别吧。 问题 《如何用Python和深度神经网络识别图像?》一文中,我给你展示了如何用深度学习,教电脑区分机器人瓦力和哆啦a梦。 很快就有用户在后台留言,问: 老师,我想自己训练一个图片分类器,到哪里去批量下载带标注的训练图像呢? 说说我写教程的时候,是如何找图片的吧。 最大的图片库,当然就是 Google 了。 在 Google 图像栏目下,键入"Walle"。 怎么样?搜索结果很符合需求吧。 你不但找到了一批高质量图片,而且它们的标注, Google 都帮你打好了。 下面一步,自然就是把这些图片下载下来了。 我让学生实际动手做,每个人找两个与别人不同的图像集合,尝试根据教程做深度学习分类。 我提供给他们的方案(几款不同的 Chrome 浏览器插件),效果都不好。 有的才下了几张,就停工,甚至把浏览器整崩溃了。 有的下载图片,都是重复的。 学生告诉我,经验证,最简单有效的方法,是一张张手动点击下载…… 这显然不是正经办法。 痛点 渴望从 Google 图片库高效批量获得优质带标注图像,不会是个案。 这个大众痛点,真的没有人尝试解决...
- 下一篇
Java并发编程笔记之FutureTask源码分析
FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。 类图结构如下所示: 线程池使用 FutureTask 时候需要注意的一点事,FutureTask 使用不当可能会造成调用线程一直阻塞,如何避免? 线程池使用 FutureTask 的时候如果拒绝策略设置为了DiscardPolicy和DiscardOldestPolicy并且在被拒绝的任务的 Future 对象上调用无参 get 方法那么调用线程会一直被阻塞。 下面先通过一个简单的例子来复现问题,代码如下: public class FutureTest { //(1)线程池单个...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- CentOS8编译安装MySQL8.0.19
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Mario游戏-低调大师作品
- CentOS6,CentOS7官方镜像安装Oracle11G