Java多态性的“飘渺之旅”
原文出处:斯武丶风晴
摘要: 如何从Java多态性进行飘渺之旅呢? 我们用例子来旅行。
1 朵星人A:人类,是一个很奇妙的物种。
2 朵星人B:他们好像分为两种,嗯 先生,以及美女?
3 朵星人C:对,更年轻的有叫 美少女的。
4 朵星人D:他们之间怎么打招呼的?我们问问AI(编译器大佬)吧。。
5 朵星人A:可以有。启动吧~
第一次启动:
1 /**
2 * 编译时多态
3 *
4 * @author Sven Augustus
5 */
6 public class StaticTest {
7
8 static abstract class Human {
9
10 }
11
12 static class Man extends StaticTest.Human {
13
14 }
15
16 static class Woman extends StaticTest.Human {
17
18 }
19
20 static class Girl extends StaticTest.Woman {
21
22 }
23
24 public void sayHello(Object guy) {
25 System.out.println("你...");
26 }
27
28 public void sayHello(Human guy) {
29 System.out.println("你好");
30 }
31
32 public void sayHello(Man guy) {
33 System.out.println("您好,先生");
34 }
35
36 public void sayHello(Woman guy) {
37 System.out.println("您好,美女");
38 }
39
40 public void sayHello(Girl guy) {
41 System.out.println("您好,美少女");
42 }
43
44 public static void main(String[] args) {
45 StaticTest test = new StaticTest();
46 StaticTest.Human manAsGuy = new StaticTest.Man();
47 StaticTest.Human womanAsGuy = new StaticTest.Woman();
48 StaticTest.Woman girlAsWoman = new StaticTest.Girl();
49 test.sayHello(manAsGuy);
50 test.sayHello(womanAsGuy);
51 test.sayHello(girlAsWoman);
52 }
53
54 }
输出:
编译器大佬告诉了他们答案。
1 朵星人众人:纳尼,他们叫的好奇怪啊?好没礼貌啊,没“您”。为毛呢??还有最后一个明明是美少女,你怎么叫美女啊?!
2 编译器大佬:你们好意思吗,你们都标为人类或美女。
3 我怎么知道他们具体是先生还是美女,亦或是美少女啊!!!所以我就只知道这么多。
StaticTest.Human manAsGuy = new StaticTest.Man();
test.sayHello(manAsGuy);
从这里,Human 称为 声明类型(也有叫静态类型) ,Man 称为 实际类型。
很清楚,现在关键在于 manAsGuy 作为 参数。
1 在编译阶段,编译器就可以根据 参数 的 声明类型(或叫静态类型) 决定使用哪个重载版本的方法。
朵星人A:说的好有道理,我们让他们自己称呼吧。
朵星人B:可以有。
朵星人C:赞同。
朵星人D:不会有问题吧?
朵星人A:不会的。就这样吧~~~
第二次启动:
1 /**
2 * 运行时多态
3 *
4 * @author Sven Augustus
5 */
6 public class DynamicTest {
7
8 static abstract class Human {
9
10 public void sayHello() {
11 System.out.println("你好");
12 }
13 }
14
15 static class Man extends DynamicTest.Human {
16
17 public void sayHello() {
18 System.out.println("您好,我是Y先生");
19 }
20 }
21
22 static class Woman extends DynamicTest.Human {
23
24 public void sayHello() {
25 System.out.println("您好,我是X美女");
26 }
27 }
28
29 public static void main(String[] args) {
30 DynamicTest.Human manAsGuy = new DynamicTest.Man();// 注释1
31 DynamicTest.Human womanAsGuy = new DynamicTest.Woman();
32 manAsGuy.sayHello();
33 womanAsGuy.sayHello();
34 }
35
36 }
输出:
编译器大佬好像去休息了,交给社会JVM回答问题。
DynamicTest.Human manAsGuy = new DynamicTest.Man();// 注释1
manAsGuy.sayHello();
这里与上个例子不同的是,manAsGuy不作为 参数,是作为引用变量 去 调用方法。
这时候,编译器只知道 引用变量manAsGuy的 静态类型,对于实际类型 就无能为力。因此在运行时由JVM方法表动态绑定。
我们发现, 引用变量调用方法 的时候,决定去调用哪个方法,是由 实际类型 在运行时确认调用哪个方法,而不是 声明类型(或叫静态类型)。 呵呵,当然这个解释还是比较勉强。我们继续。
朵星人A:咦,他们太客气了,都“您好”,还首先介绍自己,好不矜持啊。
朵星人B:地球人是这样的吗??
朵星人C:是这样的。他们不知道对方是谁,只知道自己是谁的时候是这样的。
朵星人D:好像不是啊。
朵星人A:那你说是怎样的?
朵星人D:他们需要知道对方是谁啊!
朵星人B:有道理、
朵星人C:赞同。
朵星人A:就这样吧~~~
第三次启动:
1 /**
2 * 编译时多态 和 运行时多态 混合测试
3 *
4 * @author Sven Augustus
5 */
6 public class MixTest {
7
8 static class Human {
9
10 public String sayHello(MixTest.Human human) {
11 return "你好";
12 }
13
14 public String sayHello(MixTest.Man human) {
15 return "您好,先生";
16 }
17
18 public String sayHello(MixTest.Woman human) {
19 return "您好,美女";
20 }
21
22 /*public String sayHello(MixTest.Girl human) {
23 return "您好,美少女";
24 }*/
25 }
26
27 static class Man extends MixTest.Human {
28
29 public String sayHello(MixTest.Human human) {
30 return "你好,我是Y先生";
31 }
32
33 public String sayHello(MixTest.Woman human) {
34 return "您好,美女,我是Y先生";
35 }
36
37 public String sayHello(MixTest.Girl human) {
38 return "您好,美少女,我是Y先生";
39 }
40
41 // 先生对先生比较谨慎,没那么快介绍自己 =。=
42 }
43
44 static class Woman extends MixTest.Human {
45
46 public String sayHello(MixTest.Human human) {
47 return "你好,我是X美女";
48 }
49
50 public String sayHello(MixTest.Woman human) {
51 return "您好,美女,我是X美女";
52 }
53
54 public String sayHello(MixTest.Girl human) {
55 return "您好,美少女,我是X美女";
56 }
57
58 // 美女对先生比较含蓄,没那么快介绍自己 =。=
59 }
60
61 static class Girl extends MixTest.Woman {
62
63 public String sayHello(MixTest.Human human) {
64 return "你好,我是O美少女";
65 }
66
67 }
68
69 public static void main(String[] args) {
70 MixTest test = new MixTest();
71 MixTest.Human guy = new MixTest.Human();
72 MixTest.Human manAsGuy = new MixTest.Man();
73 MixTest.Man man = new MixTest.Man();
74 MixTest.Human womanAsGuy = new MixTest.Woman();
75 MixTest.Woman woman = new MixTest.Woman();
76 MixTest.Girl girl = new MixTest.Girl();
77
78 System.out.print("假设大家在QQ等聊天软件上认识,这时候一般来招呼如下");
79 System.out.println("当然先生对先生比较谨慎,没那么快介绍自己:");
80 printMessage("一个人 欢迎 一个人", guy.sayHello(guy),
81 "[我不想你知道我的性别,我也不知道你的性别,囧]");
82 printMessage("一个人 欢迎 一名先生", guy.sayHello(man),
83 "[我不想你知道我的性别,我知道你是一名先生,嘿嘿]");
84 printMessage("一个人 欢迎 一名美女", guy.sayHello(woman),
85 "[我不想你知道我的性别,我知道你是一名美女,哈哈]");
86 printMessage("一个人[其实是先生] 欢迎 一个人", manAsGuy.sayHello(guy),
87 "[我不想你知道我的性别,但是你知道我是先生,可是我不知道你的性别,汗]");
88 printMessage("一个人[其实是先生] 欢迎 一个人[其实是先生]", manAsGuy.sayHello(manAsGuy),
89 "[我不想你知道我的性别,但是你知道我是先生,可我不知道你的性别(或许你是一名先生),呵]");
90 printMessage("一个人[其实是先生] 欢迎 一个人[其实是美女]", manAsGuy.sayHello(womanAsGuy),
91 "[我不想你知道我的性别,但是你知道我是先生,可我不知道你的性别(或许你是一名美女),嘿]");
92 printMessage("一个人[其实是先生] 欢迎 一名先生", manAsGuy.sayHello(man),
93 "[我不想你知道我的性别,但是你知道我是先生,我知道你也是一名先生,呵呵]");
94 printMessage("一个人[其实是先生] 欢迎 一名美女", manAsGuy.sayHello(woman),
95 "[我不想你知道我的性别,但是你知道我是先生,我知道你是一名美女,噢噢]");
96 printMessage("一个人[其实是先生] 欢迎 一名美少女", manAsGuy.sayHello(girl),
97 "[我不想你知道我的性别,但是你知道我是先生,我知道你是一名美少女,噢]");
98 printMessage("一名先生 欢迎 一个人 ", man.sayHello(guy),
99 "[我是一名光明磊落的先生,可我不知道你的性别,额]");
100 printMessage("一名先生 欢迎 一个人[其实是先生]", man.sayHello(manAsGuy),
101 "[我是一名光明磊落的先生,可我不知道你的性别(或许你是一名先生),咦]");
102 printMessage("一名先生 欢迎 一个人[其实是美女]", man.sayHello(womanAsGuy),
103 "[我是一名光明磊落的先生,可我不知道你的性别(或许你是一名美女),嗯]");
104 printMessage("一名先生 欢迎 一名先生", man.sayHello(man),
105 "[我是一名光明磊落的先生,我知道你也是一名先生,非常好,我先观察]");
106 printMessage("一名先生 欢迎 一名美女", man.sayHello(woman),
107 "[我是一名光明磊落的先生,我知道你是一名美女,我先介绍自己]");
108 printMessage("一名先生 欢迎 一名美少女", man.sayHello(girl),
109 "[我是一名光明磊落的先生,我知道你是一名美少女,我先礼貌介绍自己]");
110 }
111
112 private static volatile int index = 1;
113
114 private static void printMessage(String title, String message, String narrator) {
115 System.out.println((index++) + "、" + String.format("%-35s%-20s%s",
116 new String[]{title, message, narrator}));
117 }
118
119 }
输出:
社会JVM一片混沌,不知所云,乱出答案。
朵星人A:看不懂人类的世界,太复杂了吧。
朵星人B:地球人是这样的吗??
朵星人C:是这样的。他们百变。
朵星人D:额。让人类自己解读吧。
现在 这个例子 混杂了 编译时多态 和 运行时多态。
因此,我们首先观察一下,发现:
a、结果 1-3中,是 单纯的编译时多态。
b、结果 4-8 对比 10-14中,“一个人[其实是先生]” 和 “ 一名先生 ”( 引用变量) 在欢迎(方法调用) 同一个类型的人(同一静态类型参数)的时候,欢迎语是一致(调用的具体方法可能一致的?)。
c、结果9 对比 15 中,我们发现结论 b 不生效了。为什么呢?我们发现 一个人[其实是先生]” 和 “ 一名先生 ”还是有区别的。
我们仔细观察一下代码实现。
Human类有 对 Human、Man、Woman的欢迎方法
Man类有 对 Human、Woman、Girl的欢迎方法
结果9:
MixTest.Human manAsGuy = new MixTest.Man();
manAsGuy.sayHello(girl),
因为manAsGuy 声明是Human 类,方法从Human类开始搜索,Human类没有欢迎Girl的方法,
因此按照最适合方法版本,兼容找到了Human 类的欢迎Woman的方法,
又因为实际类型是Man类,该方法有重写,因此实际执行了Man类的欢迎Woman的方法。
首先定义声明类型 与 实际类型 存在向上转型的情况,称之为“动态绑定”。 如 Parent p = new Children();
我们得出了一个
方法调用步骤:
1、编译器检查引用对象的声明类型、方法名; 假设我们调用x.func(args) 方法,如果x声明为X类,那么编译器会列举X类所有名称为func的方法,以及从X类的超类继承的所有名称为func的方法。 2、接下来,编译器检查方法提供中的参数类型 如果在第1步中列举的所有func方法中找到一个 参数类型 与 args的声明类型 最为匹配的, 如果方法调用,不是动态绑定,编译器就确定调用 该func(args)方法。 如果方法调用,是动态绑定。那么继续下一步。 --------------------------------以下动态绑定------------------------------------------- 3、当程序运行并且使用动态绑定调用方法时,JVM会调用x对象实际类型相匹配的方法版本。 意思就是,如果 X x= new T();实际类型是T类,那么如果T类有定义了与第2步方法签名一致的func(args)方法,也就是重写,那么T类的该func(args)方法会被JVM实际调用,否则就在T类的超类X类中继续寻找。
Java多态性的“飘渺之旅”的更多相关文章
- JAVA 泛型意淫之旅(二)
编译器如何处理泛型 泛型类编译后长什么样? 接上文,JAVA 泛型意淫之旅1,成功迎娶白富美后,终于迎来了最振奋人心的一刻:造娃!造男娃还是造女娃?对于我们程序猿来说,谁还在乎是男娃女娃,只要是自己的 ...
- Java多态性举例说明
Java多态性的概念也可以被说成“一个接口,多个方法”. (一)相关类 class A ...{ public String show(D obj)...{ return ("A and D ...
- java多态性方法的重写Overriding和重载Overloading详解
java多态性方法的重写Overriding和重载Overloading详解 方法的重写Overriding和重载Overloading是Java多态性的不同表现.重写Overriding是父类与子类 ...
- [转载]深入理解java多态性
FROM:http://blog.csdn.net/thinkGhoster/article/details/2307001 昨天看到一个关于多态性的帖子,参考了回帖者的理解,加入了一些自己的看法,整 ...
- Java多态性详解 (父类引用子类对象)
面向对象编程有三个特征,即封装.继承和多态. 封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提下改变类的内部结构,同时保护了数据. 继承是为了重用父类代码,同时为实现多态性作准备.那么什么是多 ...
- Java多态性详解——父类引用子类对象
来源:http://blog.csdn.net/hikvision_java_gyh/article/details/8957456 面向对象编程有三个特征,即封装.继承和多态. 封装隐藏了类的内部实 ...
- Java多态性——分派
一.基本概念 Java是一门面向对象的程序设计语言,因为Java具备面向对象的三个基本特征:封装.继承和多态.这三个特征并不是各自独立的,从一定角度上看,封装和继承几乎都是为多态而准备的.多态性主要体 ...
- java多态性,父类引用指向子类对象
父类引用指向子类对象指的是: 例如父类Animal,子类Cat,Dog.其中Animal可以是类也可以是接口,Cat和Dog是继承或实现Animal的子类. Animal animal = new C ...
- java多态性
多态分两种: (1) 编译时多态(设计时多态):方法重载. (2) 运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态.(我们平时说得多的事运行时 ...
随机推荐
- model类的构造部分属性的对象 产生的json
在 action方法里, 产生一个对象,可能会是默认的全属性对象,那么在输出的 json就又所有 都出现了. 只是其他的属性全部是 null 那么 在 json里面配上 <!-- json 不产 ...
- PROFIBUS-DP
PROFIBUS – DP的DP即Decentralized Periphery.它具有高速低成本,用于设备级控制系统与分散式I/O的通信.它与PROFIBUS-PA(Process Automati ...
- 用虚拟机安装了一台Linux系统,突然想克隆一台服务器,克隆后发现无法上网,如何解决?
用虚拟机安装了一台Linux系统,突然想克隆一台服务器,克隆后发现无法上网,如何解决? 答: a.编辑网卡配置文件/etc/sysconfig/network-scripts/ifcfg-eth ...
- 【第十周】psp
代码累计 300+575+475+353+620+703=2926 随笔字数 1700+3000+3785+4210+4333+3032=20727 知识点 机器学习,支持向量机 数据库技术 Acm刷 ...
- git bash使用(markdown版)
前言 我是通过这个来学习的.个人愚笨,琢磨了半天,终于搞通了,醉了醉了,以前一直使用svn,用git确实有点水土不服.本文以如何使用git为主来展开,不涉及太多理论. git是分布式的版本管理.什么叫 ...
- Whitecoin区块链钱包高级功能使用命令
格式:NO-命令-命令概要-主要对象-参数 <必须参数> [可选参数]-含义-描述-需要未锁定钱包 1 addmultisigaddress 新增多重签名地址 地址 <nrequ ...
- Qt——线程类QThread
本文主要介绍Qt中线程类QThread的用法,参考(翻译+修改)了一篇文章:PyQt: Threading Basics Tutorial,虽然使用的是PyQt,但与C++中Qt的用法大同小异,不必太 ...
- 自平衡二叉(查找树/搜索树/排序树) binary search tree
在计算机科学中,AVL树是最先发明的自平衡二叉查找树.AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 "An alg ...
- IbatisNet连接oracle 报错
提示什么 connect oracle 1.5.0.xxxx 将你本机的oracle 客户端版本重装换成32位即可
- Linux相关——记录gdb基本操作(持续更新)
-----------2018.9.26更新标记----------- gdb的确是个很强大的东西啊,这里记录一下gdb的基本操作吧 后续可能会补充,但暂时感觉够用了就不写多了. 首先是ubuntu终 ...