java面向对象(五)之多态

多态

  面向对象编程有三大特性:封装、继承、多态。

  封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。

  继承是为了重用父类代码。两个类若存在IS-A的关系就可以使用继承。,同时继承也为实现多态做了铺垫。(我的关于继承的博客http://www.cnblogs.com/yangliguo/p/7481550.html)

  多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

例子:

  父亲person有行为这个方法,里面包括几个动作:吃饭,睡觉,走路 父亲有三个儿子,三个儿子都继承了父亲的行为方法,所以三个儿子都有吃饭,睡觉,走路这些动作,但是三个儿子又分别有自己的动作--大儿子A会弹吉他,二儿子B会唱歌,三儿子C会打鼓 ...

  1.Person person = new A(); 不是父类对象指向子类引用而是父类引用指向子类对象

  2.这个对象不能调用子类A特有的弹吉他方法--person.guitar();

  3.如果仅是这么写程序,还不是多态,记住实现多态的三要素:继承 重写 父类引用指向子类对象

  4.之后,如果你调用persion.guitar(),此时在代码的编译阶段,persion调用的仍然是自己的guitar(),不是儿子的。而当程序运行时,就是java XXX, persion调用的却是儿子的guitar()。这个动态的过程才是多态 。

Person person;  //父类的引用指向子类的方法;  person = new Student();  //person类型引用做一个判断  //(1)if(person.eat().size==2 )  {  if(person instanceof Person)  {          person.eat();  }else if(person instanceof Student)             {  Student stu = (Student)person;  stu.eat();  }             person.eat();//从代码角度看,此时是父类的引用调用的是父类中的eat方法  //(2)子类若覆盖了父类的方法,eat动态绑定到父类的引用Person上,换个名字叫动态绑定  //父类的引用可以调用子类的方法,我们把这一现象成为多态  //从字面意思来理解person这个父类的引用一会是person一会是student  //person有多种状态;  //也叫方法的动态绑定  //继承是通向多态的入口  person.f2();  person.gotobed();  person.eat();  Student stu = new Student();  stu.eat();  stu.gotobed();  //父类的引用能够调用子类的方法  }  

注:
  在继承中对象方法的调用的优先级

  this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

  即先查this对象的父类,没有就重头再查参数的父类

多态的概念

多态的定义

  指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)

实现多态的技术

  实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。

多态存在的三个必要条件

  • 继承:在多态中必须存在有继承关系的子类和父类。

  • 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。

  • 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

  对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。即超类可是使用子类方法,但不能调用父类同名方法。

多态的优点

  1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。

  2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。

  3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。

  4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。

  5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

多态的实现方式

  接口实现,继承父类进行方法重写,同一个类中进行方法重载。

例子:这个例子比较好是从知乎上看到的链接。

首先我们定义两个类,一个父类Animal,一个子类Cat。

父类Animal

class Animal {int num = 10;static int age = 20;public void eat() {System.out.println("动物吃饭");}public static void sleep() {System.out.println("动物在睡觉");}public void run(){System.out.println("动物在奔跑");}
}

子类Cat

class Cat extends Animal {int num = 80;static int age = 90;String name = "tomCat";public void eat() {System.out.println("猫吃饭");}public static void sleep() {System.out.println("猫在睡觉");}public void catchMouse() {System.out.println("猫在抓老鼠");}}

测试类Demo_Test1

class Demo_Test1 {public static void main(String[] args) {    Animal am = new Cat();am.eat();am.sleep();am.run();//am.catchMouse();这里先注释掉,等会会说明//System.out.println(am.name);//这里先注释,待会说明System.out.println(am.num);System.out.println(am.age);}
}

以上的三段代码充分体现了多态的三个前提,即:

1、存在继承关系

  Cat类继承了Animal类

2、子类要重写父类的方法

  子类重写(override)了父类的两个成员方法eat(),sleep()。其中eat()是非静态的,sleep()是静态的(static)。

3、父类数据类型的引用指向子类对象(向上转型)。

  测试类Demo_Test1中 Animal am = new Cat();语句在堆内存中开辟了子类(Cat)的对象,并把栈内存中的父类(Animal)的引用指向了这个Cat对象。到此,满足了Java多态的的必要三个前提。


运行结果:
猫吃饭。
动物在睡觉
动物在奔跑
10
20

可以看出来
  子类Cat重写了父类Animal的非静态成员方法am.eat();的输出结果为:猫吃饭。
  子类重写了父类(Animal)的静态成员方法am.sleep();的输出结果为:动物在睡觉
  未被子类(Cat)重写的父类(Animal)方法am.run()输出结果为:动物在奔跑

    System.out.println(am.num);//输出结果为10System.out.println(am.age);//输出结果为20

多态成员访问的特点

那么我们可以根据以上情况总结出多态成员访问的特点:

成员变量
编译看左边(父类),运行看左边(父类)

成员方法
编译看左边(父类),运行看右边(子类)。动态绑定

静态方法
编译看左边(父类),运行看左边(父类)。(静态和类相关,算不上重写,所以,访问还是左边的)只有非静态的成员方法,编译看左边,运行看右边

多态弊端

即多态后不能使用子类特有的属性和方法。往上面的代码看,子类Cat有一个特有的属性String name = "tomCat"; 并且还有一个特有的抓老鼠的方法catchMouse()。但是在测试类(Demo_Test)中,我们尝试调用子类特有的方法catchMouse()和打印子类特有的成员属性String name = "tomCat"; 就会报错。

am.catchMouse();
System.out.println(am.name);

  原因就是多态的弊端,就是:不能使用子类特有的成员属性和子类特有的成员方法, 因为调用method方法的时候首先回去找父类中有没有这个方法 结果父类中没有这个方法所以就会报错。

  同时父类可以使用子类方法,但不能直接调用父类同名方法。上面的例子有体现的如,am不能调用父类的eat输出“动物吃饭”。如果子类方法确实想访问父类中被隐藏的同名字段,可以用super关键字来访问它。

解决方法

  如果在代码执行过程中还想使用Cat类中特有的属性String name和它特有的成员方法catchMouse()了怎么办呢?那我们就可以把这个父类引用指向了子类对象的家伙am再强制变回Cat类型。这样am就是Cat类型的引用了,指向的也是Cat对象了,自然也能使用Cat类的一切属性和一切的成员方法。

class Demo_Test {public static void main(String[] args) {Animal am = new Cat();am.eat();am.sleep();am.run();
//  am.catchMouse();
//  System.out.println(am.name);System.out.println(am.num);System.out.println(am.age);System.out.println("------------------------------");Cat ct = (Cat)am;ct.eat();ct.sleep();ct.run();ct.catchMouse();}        
}

运行结果:

  猫吃饭
  猫在睡觉
  动物在奔跑
  猫在抓老鼠

  很明显,执行强转语句Cat ct = (Cat)am;之后,ct就指向最开始在堆内存中创建的那个Cat类型的对象了。这就是多态的魅力吧,虽然它有缺点,但是它确实十分灵活,减少多余对象的创建,不用说为了使用子类的某个方法又去重新再堆内存中开辟一个新的子类对象。

再来个例子:

  毕竟这块还是比较难理解的,这个例子大家可以看看,帮助大家加深印象。把那个总结多看看,看运行是到底要看子类还是父类(超类)

public class A {public String show(D obj) {return ("A and D");}public String show(A obj) {return ("A and A");} }public class B extends A{public String show(B obj){return ("B and B");}public String show(A obj){return ("B and A");} 
}public class C extends B{}public class D extends B{}public class Test {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

在这里看结果1、2、3还好理解,从4开始就开始糊涂了时,可以看下第一个例子下面的,哪里我写了继承链中对象方法的调用的优先级。
这个例子看不懂的话可以看下原文里面有详细解析
链接为:http://www.cnblogs.com/chenssy/p/3372798.html

转载于:https://www.cnblogs.com/yangliguo/p/7491785.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369369.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android最新知识点总结,学习android之 知识点总结

开发andorid程序必备:Eclipse 3.5以上版本SDK类似于java中的jdk。Adt 是开发Eclipe上的一个插件。组件使用:TestView:为文本设置超链接,需设置该属性andorid:autoLink”all”, 具体如下:android:id"id/myTxtView01"andr…

如何给VirtualBox虚拟机的ubuntu LVM分区扩容

我在VirtualBox安装的ubuntu里安装Cloud Foundry时遇到错误信息,磁盘空间不够了: 使用这三个命令做了清理之后,结果依然不够理想: (1) sudo apt-get autoclean(已卸载软件的安装包) (2) sudo apt-get clean…

您好GroovyFX

GroovyFX汇集了我最喜欢的两件事: Groovy和JavaFX 。 GroovyFX项目主页面将GroovyFX描述为“ [为JavaFX 2.0提供Groovy绑定”。 该页面上进一步描述了GroovyFX: GroovyFX是一个API,它使在Groovy中使用JavaFX变得更加简单和自然。 GroovyFX专…

tf 如何进行svd_Tensorflow快餐教程(6) - 矩阵分解

摘要: 特征分解,奇异值分解,Moore-Penrose广义逆矩阵分解特征向量和特征值我们在《线性代数》课学过方阵的特征向量和特征值。定义:设A∈Fnn是n阶方阵。如果存在非零向量X∈Fn1使AXλX对某个常数λ∈F成立,则称λ是A的…

calc() ---一个会计算的css属性

最近这个月一直在赶项目开发,遇到的问题和学到的前端知识没有更新到博客园,现在闲了下来,就整理一下前端知识。  在项目开发中,在样式这方面花费的时间较多,因为针对于数字的变化特别多,本人不爱记数字&a…

HashMap实现原理及源码分析

HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而H…

使用NetBeans Lambda支持在Java 8中使用Lambda表达式对列表进行排序

作为JSR 335的一部分, Lambda表达式已从Java 8开始引入Java语言,这是Java语言的一个重大变化。 如果您想了解更多关于Lambda表达式以及JSR 335的信息,可以访问以下资源: 在OpenJDK上的Lambda项目 。 Lambda常见问题解答 。 另一…

matlabapp窗口图像_matlab – 如何自定义App Designer图形的背景?

>如果要为整个图形设置纯色背景颜色,则执行此操作需要a documented way,例如:% When creating a new uifigure:fig uifigure(Color,[R G B])% if the uifigure already exists:fig.Color [R G B];>如果你想改变一些地区的背景颜色,你可以添加一个没有标题或…

qq物联网 android sdk,物联网在腾讯:QQ物联

原标题:物联网在腾讯:QQ物联在物联网方面的一些产品,作为BAT三巨头之一,腾讯自然不能落后。本文就介绍一下腾讯的物联网平台,QQ物联。QQ物联:让每个设备成为一个QQ好友QQ物联的最大特点,就是让每…

显式转换与隐式转换

显示转换 1.题目:请输入今年的年龄,求5年后多大? //a.prompt接收到的数据是string类型的。var age prompt("请输入你今年的年龄");alert(typeof age);var age5 age 5; // 这里只会拼接成了15,而不是加5alert("…

Vue-cli 搭建项目

Vue框架学习--使用 vue-cli 搭建项目vue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目,GitHub地址是:https://github.com/vuejs/vue-cli一、安装node.js 首先需要安装node环境(必须),可以直接到中文…

HTML音乐标签和滚动

<!-- 音乐标签 --><embed src"1.mp3" type""><embed src"1.mp3" type"" hidden"true"> <!-- 滚动 --><marquee behavior"alternate" >gun</marquee><marquee behavior&qu…

python程序会监控错误的语句_python装饰器实现对异常代码出现进行自动监控

异常&#xff0c;不应该存在&#xff0c;但是我们有时候会遇到这样的情况&#xff0c;比如我们监控服务器的时候&#xff0c;每一秒去采集一次信息&#xff0c;那么有一秒没有采集到我们想要的信息&#xff0c;但是下一秒采集到了&#xff0c; 而后每次的采集都能采集到&#x…

Spring Bean名称

除了未明确指定名称的情况外&#xff0c;Spring Bean名称很简单。 首先&#xff0c; 以这种方式为基于XML的bean定义指定Spring bean名称&#xff1a; <bean namesampleService1 classmvcsample.beanname.SampleService><constructor-arg><bean classmvcsample…

响应式框架Bootstrap栅格系统

Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架。Bootstrap 是基于 HTML、CSS、JAVASCRIPT 的。Bootstrap 提供了一些辅助类&#xff0c;以便更快地实现对移动设备友好的开发。这些可以通过媒体查询结合大型、小型和中型设备&#xff0c;实现内容对设备的显示和隐…

JAVA基础_泛型

什么是泛型泛型是提供给javac编译器使用的&#xff0c;可以限定集合中的输入类型&#xff0c;让编译器挡住源程序中的非法输入&#xff0c;编译器编译带类型说明的集合时会去除掉”类型”信息&#xff0c;是程序的运行效率不受影响&#xff0c;对于参数化的泛型类型&#xff0c…

python!!!!惊了,这世上居然还有这么神奇的东西存在

第一次接触到python的时候实在看学习3Blue1Brown的视频线性代数的本质的时候。惊奇的是里面的视频操作&#xff0c;例如向量的变化&#xff0c;线性变换等都是由python用代码打出来的。那时的我只是以为python是类似matlab类型的数学软件。人工智能&#xff0c;大数据的兴起&am…

int定义源码 python_python学习(第一章)

1.print输出语句# 输出字符串 print(hello) # 输出数字 print(234) print("I come from China") print("I come from China") # 输出表达式 print(71) # 将数据输出到文件中 file fp open(D:/pythoncode.txt, a) print("I come from China",…

使用Specs2和客户端API 2.0进行富有表现力的JAX-RS集成测试

毫无疑问&#xff0c; JAX-RS是一项杰出的技术。 即将发布的规范JAX-RS 2.0带来了更多的强大功能&#xff0c;尤其是在客户端API方面。 今天的帖子的主题是JAX-RS服务的集成测试。 有很多出色的测试框架&#xff0c;例如REST可以确保提供帮助&#xff0c;但是我要展示的方式是使…

android设置控件形状,Android控件自定义形状

Android中处理控件的各种形状可以用到Shape&#xff0c;ApiDemos中有相关的例子&#xff0c;在com.example.android.apis.graphics中的ShapeDrawable1类中有很详细的介绍和例子。使用xml的方法也能达到同样的效果&#xff0c;而且更加方便。如下面的代码所示&#xff1a;XML/HT…