[转帖]java基础学习总结——多态(动态绑定)
https://www.cnblogs.com/xdp-gacl/p/3644035.html 多态的概念
java基础学习总结——多态(动态绑定)
一、面向对象最核心的机制——动态绑定,也叫多态
1.1.通过下面的例子理解动态绑定,即多态
1 package javastudy.summary; 2 3 class Animal { 4 /** 5 * 声明一个私有的成员变量name。 6 */ 7 private String name; 8 9 /** 10 * 在Animal类自定义的构造方法 11 * @param name 12 */ 13 Animal(String name) { 14 this.name = name; 15 } 16 17 /** 18 * 在Animal类里面自定义一个方法enjoy 19 */ 20 public void enjoy() { 21 System.out.println("动物的叫声……"); 22 } 23 } 24 25 /** 26 * 子类Cat从父类Animal继承下来,Cat类拥有了Animal类所有的属性和方法。 27 * @author gacl 28 * 29 */ 30 class Cat extends Animal { 31 /** 32 * 在子类Cat里面定义自己的私有成员变量 33 */ 34 private String eyesColor; 35 36 /** 37 * 在子类Cat里面定义Cat类的构造方法 38 * @param n 39 * @param c 40 */ 41 Cat(String n, String c) { 42 /** 43 * 在构造方法的实现里面首先使用super调用父类Animal的构造方法Animal(String name)。 44 * 把子类对象里面的父类对象先造出来。 45 */ 46 super(n); 47 eyesColor = c; 48 } 49 50 /** 51 * 子类Cat对从父类Animal继承下来的enjoy方法不满意,在这里重写了enjoy方法。 52 */ 53 public void enjoy() { 54 System.out.println("我养的猫高兴地叫了一声……"); 55 } 56 } 57 58 /** 59 * 子类Dog从父类Animal继承下来,Dog类拥有了Animal类所有的属性和方法。 60 * @author gacl 61 * 62 */ 63 class Dog extends Animal { 64 /** 65 * 在子类Dog里面定义自己的私有成员变量 66 */ 67 private String furColor; 68 69 /** 70 * 在子类Dog里面定义Dog类的构造方法 71 * @param n 72 * @param c 73 */ 74 Dog(String n, String c) { 75 /** 76 * 在构造方法的实现里面首先使用super调用父类Animal的构造方法Animal(String name)。 77 * 把子类对象里面的父类对象先造出来。 78 */ 79 super(n); 80 furColor = c; 81 } 82 83 /** 84 * 子类Dog对从父类Animal继承下来的enjoy方法不满意,在这里重写了enjoy方法。 85 */ 86 public void enjoy() { 87 System.out.println("我养的狗高兴地叫了一声……"); 88 } 89 } 90 91 /** 92 * 子类Bird从父类Animal继承下来,Bird类拥有Animal类所有的属性和方法 93 * @author gacl 94 * 95 */ 96 class Bird extends Animal { 97 /** 98 * 在子类Bird里面定义Bird类的构造方法 99 */ 100 Bird() { 101 /** 102 * 在构造方法的实现里面首先使用super调用父类Animal的构造方法Animal(String name)。 103 * 把子类对象里面的父类对象先造出来。 104 */ 105 super("bird"); 106 } 107 108 /** 109 * 子类Bird对从父类Animal继承下来的enjoy方法不满意,在这里重写了enjoy方法。 110 */ 111 public void enjoy() { 112 System.out.println("我养的鸟高兴地叫了一声……"); 113 } 114 } 115 116 /** 117 * 定义一个类Lady(女士) 118 * @author gacl 119 * 120 */ 121 class Lady { 122 /** 123 * 定义Lady类的私有成员变量name和pet 124 */ 125 private String name; 126 private Animal pet; 127 128 /** 129 * 在Lady类里面定义自己的构造方法Lady(), 130 * 这个构造方法有两个参数,分别为String类型的name和Animal类型的pet, 131 * 这里的第二个参数设置成Animal类型可以给我们的程序带来最大的灵活性, 132 * 因为作为养宠物来说,可以养猫,养狗,养鸟,只要是你喜欢的都可以养, 133 * 因此把它设置为父类对象的引用最为灵活。 134 * 因为这个Animal类型的参数是父类对象的引用类型,因此当我们传参数的时候, 135 * 可以把这个父类的子类对象传过去,即传Dog、Cat和Bird等都可以。 136 * @param name 137 * @param pet 138 */ 139 Lady(String name, Animal pet) { 140 this.name = name; 141 this.pet = pet; 142 } 143 144 /** 145 * 在Lady类里面自定义一个方法myPetEnjoy() 146 * 方法体内是让Lady对象养的宠物自己调用自己的enjoy()方法发出自己的叫声。 147 */ 148 public void myPetEnjoy() { 149 pet.enjoy(); 150 } 151 } 152 153 public class TestPolymoph { 154 public static void main(String args[]) { 155 /** 156 * 在堆内存里面new了一只蓝猫对象出来,这个蓝猫对象里面包含有一个父类对象Animal。 157 */ 158 Cat c = new Cat("Catname", "blue"); 159 /** 160 * 在堆内存里面new了一只黑狗对象出来,这个黑狗对象里面包含有一个父类对象Animal。 161 */ 162 Dog d = new Dog("Dogname", "black"); 163 /** 164 * 在堆内存里面new了一只小鸟对象出来,这个小鸟对象里面包含有一个父类对象Animal。 165 */ 166 Bird b = new Bird(); 167 168 /** 169 * 在堆内存里面new出来3个小姑娘,名字分别是l1,l2,l3。 170 * l1养了一只宠物是c(Cat),l2养了一只宠物是d(Dog),l3养了一只宠物是b(Bird)。 171 * 注意:调用Lady类的构造方法时,传递过来的c,d,b是当成Animal来传递的, 172 * 因此使用c,d,b这三个引用对象只能访问父类Animal里面的enjoy()方法。 173 */ 174 Lady l1 = new Lady("l1", c); 175 Lady l2 = new Lady("l2", d); 176 Lady l3 = new Lady("l3", b); 177 /** 178 * 这三个小姑娘都调用myPetEnjoy()方法使自己养的宠物高兴地叫起来。 179 */ 180 l1.myPetEnjoy(); 181 l2.myPetEnjoy(); 182 l3.myPetEnjoy(); 183 } 184 }
运行结果:
1.2.画内存图理解动态绑定(多态)
首先从main方法的第一句话开始分析:
Cat c = new Cat("Catname","blue");
程序执行到这里,栈空间里有一个变量c,c里面装着一系列的值,通过这些值可以找到位于堆内存里面new出来的Cat对象。因此c是Cat对象的一个引用,通过c可以看到这个Cat对象的全部。c指向new出来的Cat对象。在new这个Cat对象的时候,调用了Cat对象的构造方法Cat(String n,String c),定义如下:
Cat(String n,String c){
super(n);
eyesColor=c;
}
因此在构造子类对象时首先使用父类对象的引用super调用父类的构造方法Animal(String name),定义如下:
Animal(String name){
this.name=name;
}
因此会把传过来的字符串“Catname”传递给父类对象的name属性。当Cat(String n,String c)构造方法调用结束后,真真正正在堆内存里面new出了一只Cat,这只Cat里面包含有父类对象Animal,这个Animal对象有自己的属性name,name属性的值为调用父类构造方法时传递过来的字符串Catname。除此之外,这只Cat还有自己的私有成员变量eyesColor,eyesColor属性的属性值为调用子类构造方法时传递过来的字符串blue。所以执行完这句话以后,内存中的布局是栈内存里面有一个引用c,c指向堆内存里面new出来的一只Cat,而这只Cat对象里面又包含有父类对象Animal,Animal对象有自己的属性name,属性值为Catname,Cat除了拥有从Animal类继承下来的name属性外,还拥有一个自己私有的属性eyesColor,属性值为blue。这就是执行完第一句话以后整个内存布局的情况如下图所示:
接着看这句话:Lady l1 = new Lady(“l1”,c);
程序执行到这里,首先在栈内存里面多了一个引用变量l1,l1里面装着一个值,通过这个值可以找到在堆内存里面new出来的Lady对象。l1就是这个Lady对象的引用,l1指向Lady对象。在创建Lady对象时,调用Lady类的构造方法:Lady(String name,Animal pet),其定义如下:
Lady(String name,Animal pet){
this.name=name;
this.pet=pet;
}
这个构造方法有两个参数,分别是String类型的name和Animal类型的pet,pet参数是一个父类对象的引用类型,这里把l1和c作为实参传递给了构造方法,接着在构造方法里面执行this.name=name,把传递过来的l1由传给Lady对象的name属性,因此Lady对象的name属性值为l1,这里也把前面new出来的那只Cat的引用c传递给了构造方法里面的参数pet,接着在构造方法里面执行this.pet=pet,pet参数又把c传过来的内容传递给Lady对象的pet属性,因此pet属性的属性值就是可以找到Cat对象的地址,因此Lady对象的pet属性也成为了Cat对象的引用对象了,通过pet里面装着的值是可以找到Cat对象的,因此pet也指向了Cat,但并不是全部指向Cat,pet指向的只是位于Cat对象内部的Animal对象,这是因为在调用构造方法时,是把c当成一个Animal对象的引用传过来的,把c作为一个Animal对象传递给了pet,所以得到的pet也是一个Animal对象的引用,因此这个pet引用指向的只能是位于Cat对象里面的Animal对象。在我pet引用对象眼里,你Cat对象就是一只普通的Animal,访问你的时候只能访问得到你里面的name属性,而你的eyesColor属性我是访问不到的,我能访问到你的name属性,访问的是位于你内部里面的父对象的name属性,因为我pet引用本身就是一个父类对象的引用,因此我可以访问父类对象的全部属性,而你子类对象Cat自己新增加的成员我pet引用是访问不了的。不过现在我pet引用不去访问你父类对象的成员变量name了,而是去访问你的成员方法enjoy了。首先是使用Lady对象的引用l1去调用Lady对象的myPetEnjoy()方法,myPetEnjoy()方法定义如下:
public void myPetEnjoy(){
pet.enjoy();
}
然后在myPetEnjoy()方法体里面又使用pet引用对象去调用父类对象里面的enjoy方法。
方法是放在代码区(code seg)里面的,里面的方法就是一句句代码。因此当使用pet引用去访问父类对象的方法时,首先是找到这个父类对象,然后看看它里面的方法到底在哪里存着,找到那个方法再去执行。这里头就比较有意思了,code seg里面有很多个enjoy方法,有父类的enjoy()方法,也有子类重写了从父类继续下来的enjoy()方法,那么调用的时候到底调用的是哪一个呢?是根据谁来确定呢?注意:这是根据你实际当中的对象来确定的,你实际当中new出来的是谁,就调用谁的enjoy方法,当你找这个方法的时候,通过pet引用能找得到这个方法,但调用代码区里面的哪一个enjoy方法不是通过引用类型来确定的,如果是通过引用类型pet来确定,那么调用的肯定是Animal的enjoy()方法,可是现在是根据实际的类型来确定,我们的程序运行以后才在堆内存里面创建出一只Cat,然后根据你实际当中new出来的类型来判断我到底应该调用哪一个enjoy()方法。如果是根据实际类型,那么调用的就应该是Cat的enjoy()方法。如果是根据引用类型,那么调用的就应该是Animal的enjoy()方法。现在动态绑定这种机制指的是实际当中new的是什么类型,就调用谁的enjoy方法。所以说虽然你是根据我父类里面的enjoy方法来调用,可是实际当中却是你new的是谁调用的就是谁的enjoy()方法。即实际当中调用的却是子类里面重写后的那个enjoy方法。当然,讲一点更深的机制,你实际当中找这个enjoy方法的时候,在父类对象的内部有一个enjoy方法的指针,指针指向代码区里面父类的Animal的enjoy方法,只不过当你new这个对象的时候,这个指针随之改变,你new的是什么对象,这个指针就指向这个对象重写后的那个enjoy方法,所以这就叫做动态绑定。只有在动起来的时候,也就是在程序运行期间,new出了这个对象了以后你才能确定到底要调用哪一个方法。我实际当中的地址才会绑定到相应的方法的地址上面,所以叫动态绑定。调这个方法的时候,只要你这个方法重写了,实际当中调哪一个,要看你实际当中new的是哪个对象,这就叫多态,也叫动态绑定。动态绑定带来莫大的好处是使程序的可扩展性达到了最好,我们原来做这个可扩展性的时候,首先都是要在方法里面判断一下这只动物是哪一类里面的动物,通过if (object instanceof class)这样的条件来判断这个new出来的对象到底是属于哪一个类里面的,如果是一只猫,就调用猫的enjoy方法,如果是一条狗,就调用狗的enjoy方法。如果我现在增加了一个Bird类,那么扩展的时候,你又得在方法里面写判断这只鸟属于哪一个类然后才能调用这只鸟的enjoy方法。每增加一个对象,你都要在方法里面增加一段判断这个对象到底属于哪个类里面的代码然后才能执行这个对象相应的方法。即每增加一个新的对象,都要改变方法里面的处理代码,而现在,你不需要再改变方法里面的处理代码了,因为有了动态绑定。你要增加哪一个对象,你实际当中把这个对象new出来就完了,不再用去修改对象的处理方法里面的代码了。也就是当你实际当中要增加别的东西的时候,很简单,你直接加上去就成了,不用去改原来的结构,你要在你们家大楼的旁边盖一个厨房,很简单,直接在旁边一盖就行了,大楼的主要支柱什么的你都不用动,这就可以让可扩展性达到了极致,这就为将来的可扩展打下了基础,也只有动态绑定(多态)这种机制能帮助我们做到这一点——让程序的可扩展性达到极致。因此动态绑定是面向对象的核心,如果没有动态绑定,那么面向对象绝对不可能发展得像现在这么流行,所以动态绑定是面向对象核心中的核心。
总结动态绑定(多态):动态绑定是指在“执行期间”(而非编译期间)判断所引用的实际对象类型,根据其实际的类型调用其相应的方法。所以实际当中找要调用的方法时是动态的去找的,new的是谁就找谁的方法,这就叫动态绑定。动态绑定帮助我们的程序的可扩展性达到了极致。
多态的存在有三个必要的条件:
- 要有继承(两个类之间存在继承关系,子类继承父类)
- 要有重写(在子类里面重写从父类继承下来的方法)
- 父类引用指向子类对象
这三个条件一旦满足,当你调用父类里面被重写的方法的时候,实际当中new的是哪个子类对象,就调用子类对象的方法(这个方法是从父类继承下来后重写后的方法)。
面向对象比较强调类和类之间,对象和对象之间的一种组织关系,如果能把这种组织关系组织得比较好的话,你的程序想扩展性比较好,比较健壮,维护性比较好这些都可以达到,关键看你的设计到底好还是不好。
[转帖]java基础学习总结——多态(动态绑定)的更多相关文章
- java基础学习总结——多态(动态绑定)
一.面向对象最核心的机制——动态绑定,也叫多态
- Java基础学习小记--多态
题外话:总结了多年的学习心得,不得不说,睡眠是一个学习者的必需品!所谓"早起毁一天"不是没有道理哪,特别对Coders来说,有几天不是加班到夜里.好吧,我承认对于初学Java的我, ...
- Java基础学习篇---------多态
一.多态性的理解 1.向上转型:子类为父类对象实例化,调用的一定是子类覆写的方法,他们之间找的是共性 2.向下转型:子类扩充了父类的某些功能,而父类中没有该功能,他们之间找的是特性 案例: Numbe ...
- 转载-java基础学习汇总
共2页: 1 2 下一页 Java制作证书的工具keytool用法总结 孤傲苍狼 2014-06-24 11:03 阅读:25751 评论:3 Java基础学习总结——Java对象的序列化和 ...
- Java基础学习-- 继承 的简单总结
代码参考:Java基础学习小记--多态 为什么要引入继承? 还是做一个媒体库,里面可以放CD,可以放DVD.如果把CD和DVD做成两个没有联系的类的话,那么在管理这个媒体库的时候,要单独做一个添加CD ...
- 尚学堂JAVA基础学习笔记
目录 尚学堂JAVA基础学习笔记 写在前面 第1章 JAVA入门 第2章 数据类型和运算符 第3章 控制语句 第4章 Java面向对象基础 1. 面向对象基础 2. 面向对象的内存分析 3. 构造方法 ...
- Java基础学习(2)
Java基础学习(二) 面向对象 对象:客观存在的事物 面向对象:人具体关注的事物的某些信息 类:是模子,确定对象会拥有的特征(属性)和行为(方法) 对象的属性:对象具有的各种特征 对象的方法:对象能 ...
- Java基础学习中一些词语和语句的使用
在Java基础学习中,我们刚接触Java会遇到一些词和语句的使用不清的情况,不能很清楚的理解它的运行效果会是怎么样的,如:break,continue在程序中运行效果及跳转位置, 1.先来看看brea ...
- Java基础学习笔记总结
Java基础学习笔记一 Java介绍 Java基础学习笔记二 Java基础语法之变量.数据类型 Java基础学习笔记三 Java基础语法之流程控制语句.循环 Java基础学习笔记四 Java基础语法之 ...
随机推荐
- 交换机配置——Cisco(思科)-交换机初始化配置
一.实验目的:完成一台交换机的初始化配置,并且可以用telnet登录 二.拓扑图如下: 三.实验步骤 1.创建一台交换机(S1)和一台主机(PC1) 2.先给PC1主机设置IP地址: 3.S1配置: ...
- MessagePack Java Jackson Dataformat 不使用 str8 数据类型的序列化
老的 msgpack-java(例如 0.6.7)并不支持 MessagePack str8 数据类型. 当你的希望的你的应用程序需要支持老的版本的话,你需要禁用这个数据类型,例如使用下面的语句: M ...
- 『ZJOI2019 D2T2』语言
~~ 话说,本题考场想出三只\(log\)的暴力做法,被卡成暴力了.~~ 题目分析 首先考虑枚举每一个点,计算这个点可以和多少点进行交易. 将所有经过该点的路径\(s,t\)拿出,那么这些极远的\(s ...
- 玩转git和github
1.概念 git---工具,版本控制 github----网站,社交平台,开源项目,远程仓库 2.下载 msysgit是Windows版的Git,从http://msysgit.github.io/下 ...
- Spring——代理工厂实现增强
借助Spring IOC的机制,为ProxyFactory代理工厂的属性实现依赖注入,这样做的优点是可配置型高,易用性好. 1.创建抽象主题 public interface ProService { ...
- AcWing:172. 立体推箱子(bfs)
立体推箱子是一个风靡世界的小游戏. 游戏地图是一个N行M列的矩阵,每个位置可能是硬地(用”.”表示).易碎地面(用”E”表示).禁地(用”#”表示).起点(用”X”表示)或终点(用”O”表示). 你的 ...
- oracle报错:The Network Adapter could not establish the connection的解决
进入oracle安装目录\product\11.2.0\dbhome_1\NETWORK\ADMIN,修改listener.ora和tnsnames.ora,修改host 我的网络IP为192.168 ...
- pytype
与mypy相比不仅可以显示错误行数,还可以看到哪个函数错误. mypy的图 pytype的图
- echarts ajax请求demo
<body> <!--为ECharts准备一个具备大小(宽高)的Dom--> <div id="main" style="width: 10 ...
- Mysql中两个select语句的连接
Mysql中两个select语句连接需要用到操作符 SQL UNION 操作符 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION 内部的 SELECT 语句必须拥 ...