Polymorphic 梦里花落知多少
Java特性
封装,继承,多态
谈到Java语言,就会自然的想到:面向对象,符合人们的思维模式。同时,它自身的特性更是被广泛的应用。三大特性:封装、继承、多态。
三大特性
► 概念理解:
1 封装
主要是用来 隐藏细节,保证安全性
2 继承
主要是用来子父类之间的继承关系,利于代码规范以及扩展
3 多态
是以继承为基础,体现的是 类与类之间的关系
多态,多态,从字面上理解就是:多种表现形态
(向上转型、向下转型)
很多人也许都有这样的意识,谈到多态,自然而然的脱口而出:重载和重写。在这里,就不纠结多态的具体含义。因为多态从其自身而言,分为了 编译时多态以及运行时多态。在这里,具体的来谈谈重载和重写。
1 重载剖析
2 重写剖析
前景引入
自己的下载的英雄联盟8个G,放到了自己定义的磁盘上了,要运行起来这游戏,而自己的内存却只有8个G。运行游戏的时候,是怎么吃我的内存的呢?一次性全部吃完 or 一点点的慢慢吃?
在Java世界里,程序的运行,必然是经过了类的加载机制:加载,验证,解析,准备,初始化,使用,卸载。其中加载的这一过程,就是Java文件编译为了.class文件。这个时候会问一下自己,这个编译的时候会发生什么事情呢?这就涉及到Javac的知识了,这里不做深究。这里只想说,如玩英雄联盟一样,这里的程序是怎样运行的?多态的两种定义:编译时多态,运行时多态到底是怎么一个区分法。重载和重写到底是属于哪一种呢?
重载 (理论以及抽象理解)
一个类里,方法名相同,参数类型以及个数不同即为重载。
1 在一个类里 模拟重载 三个同名方法 但参数不同
2 这个类,没有main方法,但是调用 javac Main类里面的main方法对这个HelloWorld进行编译
3 编译结束之后 生成.class文件
从生成的.class文件 我们可以看到
《1》 生成了都知道的 默认构造方法
默认构造方法:方法名与类名一致,没有返回值public HelloWorld() {}
《2》 同时还有三个 同名但参数不一样的方法
public void printTest() {System.out.println("无参方法:说好不哭");}public void printTest(String var1, String var2) {System.out.println("参数str: " + var1 + " 参数string: " + var2 + " 说好不哭");}public void printTest(int var1, String var2) {System.out.println("参数a: " + var1 + " 参数string: " + var2 + " 说好不哭");}
重载字节码 剖析
字节码剖析class文件
从字节码中可以看到:
默认构造方法:public HelloWorld() {}字节码:public HelloWorld();descriptor: ()V
无参方法public void printTest() {System.out.println("无参方法:说好不哭");}字节码:public void printTest();descriptor: ()V
有参方法 String var1, String var2
public void printTest(String var1, String var2) {System.out.println("参数str: " + var1 + " 参数string: " +var2 + " 说好不哭");}字节码:public void printTest(java.lang.String, java.lang.String);descriptor: (Ljava/lang/String;Ljava/lang/String;)V
有参方法 public void printTest(int var1, String var2) {System.out.println("参数a: " + var1 + " 参数string: " +var2 + " 说好不哭");}字节码:public void printTest(int, java.lang.String);descriptor: (ILjava/lang/String;)V
综上,在生成的字节码中,每个方法下面都有一个 descriptor
其实这个 descriptor 就是作为 JVM 识别的方法描述符
同一方法名 printTest()
产生的不同 方法描述符
descriptor: ()V
descriptor: (Ljava/lang/String;Ljava/lang/String;)V
descriptor: (ILjava/lang/String;)V
重载 why? 作用 以及总结
所以,为啥要有重载呢?
Java中方法的重载为 :方法名相同,参数不同的方法。
这样设计的好处?
因为 参数的有无以及参数类型的不确定性,但都是针对于同一方法名来说的。意思就是说,比如上方的,同一方法名 printTest(),都是这一动作行为的方法,但是呢?参数类型不一样。像这样的,我们就可以用重载来完成
由上述的 字节码层面看到的:
同一方法名 printTest()
产生的不同 方法描述符
所以,自己就再来自己总结一下,重载 重载 字面上看,无非就是 重新加载喽,既然是加载,是在由 java文件编译为class文件,此时虽然没有被调用,没有被运行,但是实际上 已经产生了 作为该方法的描述符descriptor,有了这个,其实在后续的调用以及运行中,JVM是根据这方法的描述符,去判断调用以及运行这同名方法中的具体其中的哪一个。
再来看看 这个类中有main方法,执行这个类里面的方法的时候
HelloWorld helloWorld = new HelloWorld();helloWorld.printTest();//调用无参方法helloWorld.printTest("杰伦","信");//调用string string 方法helloWorld.printTest(1,"信");//调用int stirng 方法
在编译的过程中,已经产生了该方法的描述符
在调用过程中,人为显现的调用(传参 or 不传参)
为什么java中重载是以方法名和参数作为方法的描述符的?
因为 在方法调用执行中 有时候 注重执行过程 而不 注重返回值,
比如构造方法,有一个默认的构造方法。但是想对属性做一下赋值,可以重载对属性操作。
所以 用方法名和方法的参数作为方法的描述符
综上 总结
Java中的方法的重载 在编译时期就已经产生了 该方法的描述符(通过字节码可知,每个方法都有自己的描述符)
在执行被调用的的时候,根据方法的描述符和传入的参数选择方法执行
------------------------------- 编译时多态
所以
重载 是 编译时多态
说完了 重载 接下来 说说 重写
重写(理论介绍 以及 抽象理解)
跟重载一样,这里自己也来说说重写,重写,重写,重新写呗,
重写Override:在Java中,
1 通过继承 extends 子父类的继承 并且只能是 单继承
可以在子类中 Override 父类中的方法
此时是重写父类中已经有的方法
2 通过实现 implements 实现接口
可以在该类中 Override 接口中定义的方法
此时是重写具体的实现方式
区分开来了以上的两点,这里谈的重写,是第一种子父类继承的重写
重载 是强调的是 同一个类里面 方法名相同 参数的类型以及个数不同
那么 重写呢?(继承的重写)
强调的是 不同的类之间的 继承 关系,重写该继承过来的方法
1 三个不同的类,模拟重写
其中 Child 继承了Parent
2 编译后,生成了三个class文件
从生成的.class文件 我们可以看到
1 HelloWorld 类
public class HelloWorld {public HelloWorld() {}
}
2 Parent 类
class Parent {Parent() {}public void testSay(String var1) {System.out.println("父亲说:说好不哭");}
3 Child 类 (继承了Parent ) class Child extends Parent {Child() {}public void testSay(String var1) {System.out.println("孩子说:说好不哭");}
}
重写 字节码
从字节码文件 我们可以看到
1 HelloWorldpublic HelloWorld();descriptor: ()V
2 Parentpublic void testSay(java.lang.String);descriptor: (Ljava/lang/String;)V
3 Child(继承了Parent) public void testSay(java.lang.String);descriptor: (Ljava/lang/String;)V
同样都会有 descriptor
这个时候,是否会问下:重写也是编译时多态?
其实 这里的 descriptor 并不是判断依据
由上面的可以知道,重载 是编译时多态,是由于在同一个类中,同一的方法名 不同的参数 在编译的时候,就已经确定好了,在调用的时候,通过传递参数就可以具体的指定为 是哪一个方法了。
那么,重写呢?
重写 现在知道的是,在不同的类中,子父类,同样的方法名,子类可以继承后,方法名以及参数不改,修改的是 方法体里面的内容。那么在调用的时候,是否可以知道其具体的是哪一个方法呢?
猜想:如果知道是 具体调用的是哪一个方法,那么就是编译时多态
否则,就为 运行时多态
既然是 子父类继承,那么父类永远只有一个,但是孩子可以是多个
//这里模拟的是 父类的引用 指向到底是哪一个子类的对象Parent parent = h.testWay();//然后 子类对象的方法打印parent.testSay("杰伦");
打印结果剖析:
1 由编译时多态(重载)的结论。在调用的时候,根据参数的不同,就已经知道具体的是要调用的是哪一个方法了
2 那么在重写的时候,这个时候调用的时候,由于是子父类继承,方法名以及参数是一样的,这里的关注点是,在调用的时候,是否就已经知道调用的是具体的哪一个方法
3 打印的结果:
多次运行main方法
《1》孩子3说:说好不哭
《2》孩子1说:说好不哭
《3》孩子2说:说好不哭
《4》孩子1说:说好不哭
通过以上的打印结果,运行了 四次 main方法
每次打印的结果都不一样,这个时候能说,重写是 编译时多态吗?因为 编译时多态,不管运行多少次,打印的结果都是一样的
所以:
重写(继承关系的重写) 是 运行时多态,因为在编译的时候,确定不好。
重写 why? 作用 以及总结
综上,重写(继承关系的重写) 是 运行时多态。
//这里模拟的是 父类的引用 指向到底是哪一个子类的对象Parent parent = h.testWay();
所以 运行时多态 : 父类的引用 指向了 子类的对象
同时 在撸过的代码中
List<> list = new ArrayList<>();
应该说 都遇到过这种写法
撸过ArrayList源码都知道,ArrayList 实现了List接口
所以 接口的引用 指向了 实现该接口的类的对象
这里为啥不是
List<> list = new List<>();
或者
ArrayList<> list = new ArrayList<>();
因为这两种 都是 实例化当前类,是具体的。
设计模式中 "代码尽量依赖于抽象,不依赖于具体"
面向接口,依赖抽象,才是符合思维模式,代码可以方便替换。
再来谈谈 关于重写(继承关系的重写)注意的地方:
1 父类的引用指向子类的对象
至于会打印出来什么?
理解了 父类的引用 指向了 子类的对象
就可以大声的喊出来,打印的会是什么了
2 父类的引用指向子类的对象,但是调用的方法是子类的,父类没有
这个时候,会问 是否可以编译通过,以及会打印什么
其实,理解了 父类的引用 指向了 子类的对象
就可以大声的说出来了
当 父类的引用 指向了 子类的对象 时,调用的方法 一定要满足 子父类二者中的方法都要有
向上转型 向下转型
说到这个 其实就是形态状态的改变
向上转型 (子类 继承了 父类 ,有了 父类里面的所有方法,子类还可以 定义自己的方法 所以 子类的作用方法范围 必然比 父类大)
所以 这里的 向上,指的是 范围的意思
既然是 向上(范围变大了) 所以 就是 父类引用 ---指向--》 子类对象
Parent p = new Child();//这就叫 upcasting (向上转型)
// 现在 父类引用p 引用指向一个Child 子类对象
那相应的
向下转型
既然是 向下(范围变小了 必须要强转) 所以 就是 子类引用 ---指向---》子类对象的父类引用
Child c= (Child )p;
// 这就叫 downcasting (向下转型)
// 现在p 还是指向 Child 子类对象
注意:
子类的引用 不能指向 父类对象
不管怎样,通过这样的分析
就可以明确的了解到了
1 多态 ---- 编译时多态 运行时多态
2 向上 向下转型
在其他地方,有的也会有说 :多态 是运行时 父类引用指向子类对象。是类与类的关系,而重载 是在一个类中的。所以 重载不是多态。甚至还有 “晚绑定”,“早绑定”。
在这里呢,多态这个概念 每个人的理解不一样,但是,对于原理一定要掌握
所以 Polymorphic 梦里花落知多少,知多知少,原理最重要~
Hey~
我们又见面啦~
你还好吗?
2019.09.22
喜欢记得来一个
Polymorphic 梦里花落知多少相关推荐
- 《梦里花落知多少》-三毛
离开人世的时候,可以得到一个土做的馒头,第一次看到把死亡写的这么豁达俏皮 这是读的三毛的第一本书, 短篇文集,记录了三毛的爱人死后三毛的生活, 三毛是个善良的人, 通过百科知道三毛最后是自杀的,可能她 ...
- 《梦里花落知多少》的经典语句(怀念)
<梦里花落知多少>的经典语句(怀念)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:of ...
- 梦里花落知多少………
梦里花落知多少--- 生活就是这样,永远占据着绝对的地位,当无数的傻子高呼着自己控制了生活并掌握了命运,却没有看到,生活站在更高的苍穹之上,露出讥笑与嘲讽的面容. -------<梦里花落知多少 ...
- 梦里花落知多少,网络抖动逃不了
作者 / 丁雪峰(花名:玄拙) 编辑 / 伯瑜 芹菜 出品 / 云巅论剑 食堂 "喂,喂,喂......" 睦晨回过神来,不好意思地对边上已经谢了顶的同事笑了笑. 同事安慰他:&q ...
- 梦里花落知多少,真性情是三毛
[img]http://dl.iteye.com/upload/attachment/608500/4d506c63-786d-3dba-8073-597ba6b519da.jpg[/img] &qu ...
- 每日一皮:昨晚梦见男朋友和别的女人在逛街,梦里我的第一反应就是查源代码......
网络上女程序员发的一条微博: "昨晚梦见男朋友和别的女人在逛街,梦里我的第一反应就是查源代码,结果调试半天查不出来为什么显示的那个女人不是我,最后含泪把那个女人注释掉了,再一运行就是我男朋友 ...
- 梦里梦到的人是谁在想谁?
梦里梦到某人,某人和我,到底是谁在想谁? 假定做梦时梦里出现的人就是因为想念,那么,要想回答这个问题,我看其实不难. 1.做梦到底是怎么回事,真的是因为思念.想念吗?是谁在思念.想念谁? 做梦究竟是怎 ...
- 梦里看到你为我写的情书
梦里看到你为我写的情书 --代腾飞 2009年11月26日 于成都 夜深无数 泪光伴着蜡烛 心中的情总是压抑不住 曾经的往事一一流出 笔尖下的墨早已化作了无尽的情书 你说 要让我们曾经的朝朝暮暮在文字 ...
- 原神梦里花花种在哪种
原神梦里花花种在哪种,游戏也是即将开启新活动--梦里生花,那么玩家们所获取的"梦里花"花种又应该种植在哪里呢,相信还有些小伙伴不清楚.所以下面就为大家带来了梦里花花种的种植地点介绍 ...
最新文章
- vfork 挂掉的一个问题
- 使用Android Studio搭建Android集成开发环境(图文教程)
- 【AI产品】产品小姐姐分析抖音背后的计算机视觉技术
- HDU 6264 Super-palindrome
- Jerry Wang的微信小程序开发系列文章
- c++ udp通信_Web 通信协议,你还需要知道:SPDY 和 QUIC
- html文件脚本,我想要从html文件或js脚本执行jsx脚本
- MongoDB实战系列之二:MongoDB的常用操作
- Knockout.Js官网学习(创建自定义绑定)
- 10个Android开发必看的GitHub开源项目
- autojs识别数字ocr
- Go开发 之 单端口转发到多个端口
- SpringMVC 通过Map、Model和ModelMap向页面传值
- 32位系统无法运行64位系统安装文件
- sigmoid函数sigmoid求导
- 在键盘上输入数n,编程计算sum=1!+2!+··· + n!的结果
- unity3d横版游戏移动_制作游戏并不困难。 回顾Unity3D上的小型移动项目
- 作为打工人,普通人努力的意义何在?
- cisco动态路由(OSPF协议)设计计算机网络课设/实验 (含.pkt文件)
- 【转载】选择性搜索算法介绍——Selective Search