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 父类的引用指向子类的对象

至于会打印出来什么?

理解了 父类的引用  指向了 子类的对象

就可以大声的喊出来,打印的会是什么了

父类的引用指向子类的对象,但是调用的方法是子类的,父类没有

这个时候,会问 是否可以编译通过,以及会打印什么

其实,理解了 父类的引用  指向了 子类的对象

就可以大声的说出来了

当 父类的引用  指向了 子类的对象 时,调用的方法 一定要满足 子父类二者中的方法都要有

向上转型  向下转型

说到这个 其实就是形态状态的改变

向上转型   (子类 继承了 父类  ,有了 父类里面的所有方法,子类还可以 定义自己的方法 所以 子类的作用方法范围 必然比 父类大)

所以 这里的 向上,指的是  范围的意思

既然是 向上(范围变大了)  所以 就是   父类引用 ---指向--》 子类对象

Parent p = new Child();//这就叫 upcasting (向上转型)
// 现在 父类引用p 引用指向一个Child 子类对象

那相应的

向下转型

既然是 向下(范围变小了 必须要强转)  所以 就是   子类引用 ---指向---》子类对象的父类引用

Child c= (Child )p;
// 这就叫 downcasting (向下转型)
// 现在p 还是指向 Child 子类对象

注意:

子类的引用 不能指向 父类对象

不管怎样,通过这样的分析

就可以明确的了解到了

1 多态 ----  编译时多态   运行时多态

2 向上 向下转型

在其他地方,有的也会有说 :多态 是运行时 父类引用指向子类对象。是类与类的关系,而重载 是在一个类中的。所以 重载不是多态。甚至还有 “晚绑定”,“早绑定”。

在这里呢,多态这个概念 每个人的理解不一样,但是,对于原理一定要掌握

所以 Polymorphic 梦里花落知多少,知多知少,原理最重要~

Hey~

我们又见面啦~

你还好吗?

2019.09.22

喜欢记得来一个

Polymorphic 梦里花落知多少相关推荐

  1. 《梦里花落知多少》-三毛

    离开人世的时候,可以得到一个土做的馒头,第一次看到把死亡写的这么豁达俏皮 这是读的三毛的第一本书, 短篇文集,记录了三毛的爱人死后三毛的生活, 三毛是个善良的人, 通过百科知道三毛最后是自杀的,可能她 ...

  2. 《梦里花落知多少》的经典语句(怀念)

    <梦里花落知多少>的经典语句(怀念)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:of ...

  3. 梦里花落知多少………

    梦里花落知多少--- 生活就是这样,永远占据着绝对的地位,当无数的傻子高呼着自己控制了生活并掌握了命运,却没有看到,生活站在更高的苍穹之上,露出讥笑与嘲讽的面容. -------<梦里花落知多少 ...

  4. 梦里花落知多少,网络抖动逃不了

    作者 / 丁雪峰(花名:玄拙) 编辑 / 伯瑜 芹菜 出品 / 云巅论剑 食堂 "喂,喂,喂......" 睦晨回过神来,不好意思地对边上已经谢了顶的同事笑了笑. 同事安慰他:&q ...

  5. 梦里花落知多少,真性情是三毛

    [img]http://dl.iteye.com/upload/attachment/608500/4d506c63-786d-3dba-8073-597ba6b519da.jpg[/img] &qu ...

  6. 每日一皮:昨晚梦见男朋友和别的女人在逛街,梦里我的第一反应就是查源代码......

    网络上女程序员发的一条微博: "昨晚梦见男朋友和别的女人在逛街,梦里我的第一反应就是查源代码,结果调试半天查不出来为什么显示的那个女人不是我,最后含泪把那个女人注释掉了,再一运行就是我男朋友 ...

  7. 梦里梦到的人是谁在想谁?

    梦里梦到某人,某人和我,到底是谁在想谁? 假定做梦时梦里出现的人就是因为想念,那么,要想回答这个问题,我看其实不难. 1.做梦到底是怎么回事,真的是因为思念.想念吗?是谁在思念.想念谁? 做梦究竟是怎 ...

  8. 梦里看到你为我写的情书

    梦里看到你为我写的情书 --代腾飞 2009年11月26日 于成都 夜深无数 泪光伴着蜡烛 心中的情总是压抑不住 曾经的往事一一流出 笔尖下的墨早已化作了无尽的情书 你说 要让我们曾经的朝朝暮暮在文字 ...

  9. 原神梦里花花种在哪种

    原神梦里花花种在哪种,游戏也是即将开启新活动--梦里生花,那么玩家们所获取的"梦里花"花种又应该种植在哪里呢,相信还有些小伙伴不清楚.所以下面就为大家带来了梦里花花种的种植地点介绍 ...

最新文章

  1. vfork 挂掉的一个问题
  2. 使用Android Studio搭建Android集成开发环境(图文教程)
  3. 【AI产品】产品小姐姐分析抖音背后的计算机视觉技术
  4. HDU 6264 Super-palindrome
  5. Jerry Wang的微信小程序开发系列文章
  6. c++ udp通信_Web 通信协议,你还需要知道:SPDY 和 QUIC
  7. html文件脚本,我想要从html文件或js脚本执行jsx脚本
  8. MongoDB实战系列之二:MongoDB的常用操作
  9. Knockout.Js官网学习(创建自定义绑定)
  10. 10个Android开发必看的GitHub开源项目
  11. autojs识别数字ocr
  12. Go开发 之 单端口转发到多个端口
  13. SpringMVC 通过Map、Model和ModelMap向页面传值
  14. 32位系统无法运行64位系统安装文件
  15. sigmoid函数sigmoid求导
  16. 在键盘上输入数n,编程计算sum=1!+2!+··· + n!的结果
  17. unity3d横版游戏移动_制作游戏并不困难。 回顾Unity3D上的小型移动项目
  18. 作为打工人,普通人努力的意义何在?
  19. cisco动态路由(OSPF协议)设计计算机网络课设/实验 (含.pkt文件)
  20. 【转载】选择性搜索算法介绍——Selective Search

热门文章

  1. 通过抓包获取内涵段子的接口
  2. vue的sync修饰符
  3. 将16进制表示的编码转为中文
  4. java 内存模型面试_Java面试- JVM 内存模型讲解
  5. java面试技术准备
  6. 法国计算机专业学校排名,法国计算机专业大学排名(2020年泰晤士)_快飞留学
  7. 他们如何利用微信赚钱
  8. Thymeleaf全解
  9. 【杂谈】如何找回自己的执行力
  10. 解决数值输入框可以输入字母E的问题