博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多态性的“飘渺之旅”
阅读量:5738 次
发布时间:2019-06-18

本文共 7208 字,大约阅读时间需要 24 分钟。

hot3.png

朵星人A:人类,是一个很奇妙的物种。朵星人B:他们好像分为两种,嗯 先生,以及美女?朵星人C:对,更年轻的有叫 美少女的。朵星人D:他们之间怎么打招呼的?我们问问AI(编译器大佬)吧。。朵星人A:可以有。启动吧~

 

第一次启动:

/** * 编译时多态 * * @author Sven Augustus */public class StaticTest {	static abstract class Human {	}	static class Man extends StaticTest.Human {	}	static class Woman extends StaticTest.Human {	}	static class Girl extends StaticTest.Woman {	}	public void sayHello(Object guy) {		System.out.println("你...");	}	public void sayHello(Human guy) {		System.out.println("你好");	}	public void sayHello(Man guy) {		System.out.println("您好,先生");	}	public void sayHello(Woman guy) {		System.out.println("您好,美女");	}	public void sayHello(Girl guy) {		System.out.println("您好,美少女");	}	public static void main(String[] args) {		StaticTest test = new StaticTest();		StaticTest.Human manAsGuy = new StaticTest.Man();		StaticTest.Human womanAsGuy = new StaticTest.Woman();		StaticTest.Woman girlAsWoman = new StaticTest.Girl();		test.sayHello(manAsGuy);		test.sayHello(womanAsGuy);		test.sayHello(girlAsWoman);	}}

输出:

124629_OcMM_155593.png

 编译器大佬告诉了他们答案。

朵星人众人:纳尼,他们叫的好奇怪啊?好没礼貌啊,没“您”。为毛呢??还有最后一个明明是美少女,你怎么叫美女啊?!编译器大佬:你们好意思吗,你们都标为人类或美女。          我怎么知道他们具体是先生还是美女,亦或是美少女啊!!!所以我就只知道这么多。

StaticTest.Human manAsGuy = new StaticTest.Man();

test.sayHello(manAsGuy);

从这里,Human 称为 声明类型(也有叫静态类型) Man 称为 实际类型

很清楚,现在关键在于 manAsGuy 作为 参数。

在编译阶段,编译器就可以根据 参数  声明类型(或叫静态类型 决定使用哪个重载版本的方法。

朵星人A:说的好有道理,我们让他们自己称呼吧。朵星人B:可以有。朵星人C:赞同。朵星人D:不会有问题吧?朵星人A:不会的。就这样吧~~~

 

第二次启动:

/** * 运行时多态 * * @author Sven Augustus */public class DynamicTest {	static abstract class Human {		public void sayHello() {			System.out.println("你好");		}	}	static class Man extends DynamicTest.Human {		public void sayHello() {			System.out.println("您好,我是Y先生");		}	}	static class Woman extends DynamicTest.Human {		public void sayHello() {			System.out.println("您好,我是X美女");		}	}	public static void main(String[] args) {		DynamicTest.Human manAsGuy = new DynamicTest.Man();// 注释1		DynamicTest.Human womanAsGuy = new DynamicTest.Woman();		manAsGuy.sayHello();		womanAsGuy.sayHello();	}	}

输出:

124818_yqvB_155593.png

编译器大佬好像去休息了,交给社会JVM回答问题。

 

DynamicTest.Human manAsGuy = new DynamicTest.Man();// 注释1

manAsGuy.sayHello();

这里与上个例子不同的是,manAsGuy不作为 参数,是作为引用变量 去 调用方法。

这时候,编译器只知道 引用变量manAsGuy的 静态类型,对于实际类型 就无能为力。因此在运行时由JVM方法表动态绑定。

我们发现,     

引用变量调用方法 的时候,决定去调用哪个方法,是 实际类型 在运行时确认调用哪个方法,而不是 声明类型(或叫静态类型)。

呵呵,当然这个解释还是比较勉强。我们继续。

朵星人A:咦,他们太客气了,都“您好”,还首先介绍自己,好不矜持啊。朵星人B:地球人是这样的吗??朵星人C:是这样的。他们不知道对方是谁,只知道自己是谁的时候是这样的。朵星人D:好像不是啊。朵星人A:那你说是怎样的?朵星人D:他们需要知道对方是谁啊!朵星人B:有道理、朵星人C:赞同。朵星人A:就这样吧~~~

 

第三次启动:

/** * 编译时多态 和 运行时多态 混合测试 * * @author Sven Augustus */public class MixTest {	static class Human {		public String sayHello(MixTest.Human human) {			return "你好";		}		public String sayHello(MixTest.Man human) {			return "您好,先生";		}		public String sayHello(MixTest.Woman human) {			return "您好,美女";		}		/*public String sayHello(MixTest.Girl human) {			return "您好,美少女";		}*/	}	static class Man extends MixTest.Human {		public String sayHello(MixTest.Human human) {			return "你好,我是Y先生";		}		public String sayHello(MixTest.Woman human) {			return "您好,美女,我是Y先生";		}		public String sayHello(MixTest.Girl human) {			return "您好,美少女,我是Y先生";		}		// 先生对先生比较谨慎,没那么快介绍自己 =。=	}	static class Woman extends MixTest.Human {		public String sayHello(MixTest.Human human) {			return "你好,我是X美女";		}		public String sayHello(MixTest.Woman human) {			return "您好,美女,我是X美女";		}		public String sayHello(MixTest.Girl human) {			return "您好,美少女,我是X美女";		}		// 美女对先生比较含蓄,没那么快介绍自己 =。=	}	static class Girl extends MixTest.Woman {		public String sayHello(MixTest.Human human) {			return "你好,我是O美少女";		}	}	public static void main(String[] args) {		MixTest test = new MixTest();		MixTest.Human guy = new MixTest.Human();		MixTest.Human manAsGuy = new MixTest.Man();		MixTest.Man man = new MixTest.Man();		MixTest.Human womanAsGuy = new MixTest.Woman();		MixTest.Woman woman = new MixTest.Woman();		MixTest.Girl girl = new MixTest.Girl();		System.out.print("假设大家在QQ等聊天软件上认识,这时候一般来招呼如下");		System.out.println("当然先生对先生比较谨慎,没那么快介绍自己:");		printMessage("一个人 欢迎 一个人", guy.sayHello(guy),                      "[我不想你知道我的性别,我也不知道你的性别,囧]");		printMessage("一个人 欢迎 一名先生", guy.sayHello(man),                     "[我不想你知道我的性别,我知道你是一名先生,嘿嘿]");		printMessage("一个人 欢迎 一名美女", guy.sayHello(woman),                      "[我不想你知道我的性别,我知道你是一名美女,哈哈]");		printMessage("一个人[其实是先生] 欢迎 一个人", manAsGuy.sayHello(guy),                      "[我不想你知道我的性别,但是你知道我是先生,可是我不知道你的性别,汗]");		printMessage("一个人[其实是先生] 欢迎 一个人[其实是先生]", manAsGuy.sayHello(manAsGuy),                      "[我不想你知道我的性别,但是你知道我是先生,可我不知道你的性别(或许你是一名先生),呵]");		printMessage("一个人[其实是先生] 欢迎 一个人[其实是美女]", manAsGuy.sayHello(womanAsGuy),                      "[我不想你知道我的性别,但是你知道我是先生,可我不知道你的性别(或许你是一名美女),嘿]");		printMessage("一个人[其实是先生] 欢迎 一名先生", manAsGuy.sayHello(man),                      "[我不想你知道我的性别,但是你知道我是先生,我知道你也是一名先生,呵呵]");		printMessage("一个人[其实是先生] 欢迎 一名美女", manAsGuy.sayHello(woman),                      "[我不想你知道我的性别,但是你知道我是先生,我知道你是一名美女,噢噢]");		printMessage("一个人[其实是先生] 欢迎 一名美少女", manAsGuy.sayHello(girl),                      "[我不想你知道我的性别,但是你知道我是先生,我知道你是一名美少女,噢]");		printMessage("一名先生 欢迎 一个人 ", man.sayHello(guy),                     "[我是一名光明磊落的先生,可我不知道你的性别,额]");		printMessage("一名先生 欢迎 一个人[其实是先生]", man.sayHello(manAsGuy),                      "[我是一名光明磊落的先生,可我不知道你的性别(或许你是一名先生),咦]");		printMessage("一名先生 欢迎 一个人[其实是美女]", man.sayHello(womanAsGuy),                      "[我是一名光明磊落的先生,可我不知道你的性别(或许你是一名美女),嗯]");		printMessage("一名先生 欢迎 一名先生", man.sayHello(man),                      "[我是一名光明磊落的先生,我知道你也是一名先生,非常好,我先观察]");		printMessage("一名先生 欢迎 一名美女", man.sayHello(woman),                      "[我是一名光明磊落的先生,我知道你是一名美女,我先介绍自己]");		printMessage("一名先生 欢迎 一名美少女", man.sayHello(girl),                     "[我是一名光明磊落的先生,我知道你是一名美少女,我先礼貌介绍自己]");	}	private static volatile int index = 1;	private static void printMessage(String title, String message, String narrator) {		System.out.println((index++) + "、" + String.format("%-35s%-20s%s",                                                            new String[]{title, message, narrator}));	}}

输出:

125204_OICY_155593.png

社会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类中继续寻找。

 

 

转载于:https://my.oschina.net/langxSpirit/blog/1541074

你可能感兴趣的文章
从 WTForm 的 URLXSS 谈开源组件的安全性
查看>>
Android Transition Note
查看>>
依赖注入实现组件化
查看>>
爬虫模拟登录—OAUTH的详解
查看>>
Node 学习 -- jade
查看>>
推荐简约漂亮的小程序插件 calendar
查看>>
Runtime源码 Category(分类)
查看>>
用 Kotlin 开发 Android 项目是一种什么样的感受?(二)
查看>>
Laravel 5.4 Mix执行 npm run dev时报错,提示cross-env找不到(not found)的原因及解决办法...
查看>>
五、Android性能优化之UI卡顿分析之内存抖动和计算性能优化
查看>>
RESTful在路由url有名义的写法
查看>>
并发编程之多线程基础
查看>>
JavaScript数组去重的12种方法
查看>>
JavaScript 函数式编程
查看>>
Redis 学习笔记
查看>>
隐式类型转换
查看>>
hello zapi 项目准备工作-CPP开发PHP之旅第一节
查看>>
vue中的三要素,你知道几个呢?
查看>>
获取iphone的系统信息
查看>>
node数据加密,还不快快点进来?
查看>>