[短文速读] 重载有暗坑,JVM是如何执行方法的
前言
这将是一个系列文章。原因是自己写了很多文章,也看了很多文章。从最开始的仅仅充当学习笔记,到现在认认真真去写文章去分享。中间发现了很多事情,其中最大发现是:收藏不看!总是想着先收藏以后有时间再看,到后来…大家都懂得。大多数文章仿佛石沉大海,失去了应有的价值。
因为技术文章大多需要比较重的思考,但是现如今时间碎片化很严重,因此收藏不看也实属不得已。所以萌生了这个系列的想法,系列文章的特点:以一些日常开发中不起眼的基础知识点为内核,围绕此包裹通俗易懂的文字。尽量用少思考的模式去讲述一个知识。让我们能够真正在碎片化的时间里学到东西!
出场角色
小A:刚踏入Java编程之路…
MDove:一个快吃不上饭的Android开发…
正题
引子
小A:MDove,我最近遇到一个问题百思不得其解。
MDove:正常,毕竟你这智商1+1都不知道为什么等于2。
小A:那1+1为啥等于2呢?
MDove:......说你遇到的问题。
重载不理解
小A:是这样的,我在学习多态的时候,重载和重写,有点蒙圈了...
public class MethodMain {public static void main(String[] args) {MethodMain main = new MethodMain();Language language = new MethodMain().new Java();Language java = new MethodMain().new Java();main.sayHi(language);main.sayHi(java);}private void sayHi(Java java) {System.out.println("Hi Java");}private void sayHi(Language language) {System.out.println("Im Language");}public class Java extends Language {}public abstract class Language {}
}
小A:程序运行结果为什么是这个呀?我觉得它应该一个是Im Language一个是Hi Java呀。
MDove:原来是这个疑惑呀。好,那今天就好好聊一聊重载/重写背后:方法调用的原理。为了更好理解,我尽量不用学术性强的语言来解释。开始之前让我们先看一行代码:
如果想了解更专业的内容,可以参考《Java虚拟机规范》或者《深入理解Java虚拟机》。
A a = new B();
MDove:对于A和B来说,他们有不同的学术名词。A称之为静态类型,B称之为实际类型。对于Language language = new MethodMain().new Java();
也是如此:Language是静态类型,Java是实际类型。
MDove:从你写的demo里,我们可以看出来:main.sayHi(language); main.sayHi(java);
最终都是调用了private void sayHi(Language language)
。我们是不是可以得出一个结论:方法的调用是根据静态类型去匹配的?
就像你的那个demo一样,language和java的静态类型都是Language所以就匹配了private void sayHi(Language language)
这个方法。
重写不明白
小A:不对啊!!!如果用Override重写的话,这个结论是不成立的!按照静态类型的说法,那下面那个demo,的调用者的静态类型是Language应该打印Hi,Im Language。
public class MethodMain {public static void main(String[] args) {Language language = new MethodMain().new Java();language.sayHi();}public class Java extends Language {@Overridepublic void sayHi() {System.out.println("Hi,Im Java");}}public class Language {public void sayHi() {System.out.println("Hi,Im Language");}}
}
MDove:别急,你这是面向对象多态神经紊乱综合征。说白了就是看串了。你难道不觉得,这俩个demo写法上有不同么?或者再上升一下重载和重写是不是有不同之处?
小A:你这么一说好像真是!重载是在一个类里边折腾;而重写是子类折腾父类。
MDove:没错,正式如此。导致了JVM在加载方法的时候采用了不同的方式。因此也就有了你所感到疑惑的,为什么重载会是这种结果,而重写会是那种结果。
小A:那可不可以最多讲一讲加载方法的不同之处的?
JVM如何调用方法
MDove:在调用之前,我们再回到上文提到的静态类型上。对于JVM来说,在编译期变量的静态类型是确定的,同样重载的方法也就能够确定。很好理解,因为二者都是确定无误的。所以对于这种方法,JVM采用静态分派的方式去调用。(因为这类不涉及任何需要动态决定类型的地方)
MDove:说白了就是,在编译期就决定好该怎么调用这个方法。因此对于在运行期间生成的实际类型JVM是不关心的。只要你的静态类型是郭德纲,就算你new一个吴亦凡出来。这行代码也不能又长又宽...
小A:照这个逻辑来说,重写就是动态分派,需要JVM在运行期间确定对象的实际类型,然后再决定调用哪个方法。
MDove:没错,毕竟重写涉及到你是调用子类的方法还是调用父类。也就是说调用者的类型对于重写是有影响的,因此这种情况下静态分派就行不通了,需要在运行期间去决定。
MDove:当然我们用嘴说是很轻巧的,实际JVM去执行时是很复杂的过程。如果你感兴趣可以去了解这方面的知识。简单来说,虚拟机在执行对应的字节码时,变量压栈之后,会去找它的实际类型,然后在将它的符号引用映射到真正的方法地址上,也就是我们子类重写的方法上。
重载的暗坑
MDove:因为重载的性质,重载在可变参数上是有坑的。我写的demo,你瞅瞅能不能感觉出奇怪的地方:
public class MethodMain {public static void main(String[] args) {MethodMain main = new MethodMain();main.fun(null, 666);main.fun(null, 666, 666);}private void fun(Object obj, Object... args) {System.out.println("fun(Object obj, Object... args)");}private void fun(String string, Object obj, Object... args) {System.out.println("fun(String string, Object obj, Object... args)");}
}
小A:我觉得应该是打印:fun(Object obj, Object... args)和fun(String string, Object obj, Object... args)吧?
MDove:最开始我也是这么认为的。我们从我们的角度出发,很自然的认为main.fun(null, 666);
应该调用private void fun(Object obj, Object... args)
,而main.fun(null, 666, 666);
去调用private void fun(String string, Object obj, Object... args)
。
MDove:可以如果我们站在程序的角度呢?因为我们写的是可变参数,程序怎么可能知道666和666,666应该去对应哪个方法。所以这个demo的结果是:
小A:那我有一个疑问,既然程序很难洞察应该调用哪个可变参数的方法,那它又是为什么调用了下边的而不是上边的呢?
MDove:那是因为编译期在匹配方法时,如果有多个可能性,它会使用更向下的类型,结合上述的demo。因为我们传入null时,虽然即能满足Object又能满足String。但由于String是 Object的子类(也就是更向下),因此编译器会认为第二个方法更为贴切。
小A:Skr,Skr...
static重写
MDove:我们继续聊一聊重写,咱们说了普通的重写。静态的重写岂能不提。首先来说static不能称之为重写,只能叫做隐藏父类实现。文字很抽象,直接看代码:
public class MethodMain {public static void main(String[] args) {Language.sayHi();Java.sayHi();}
}public class Java extends Language {public static void sayHi() {System.out.println("Hi,Im Java");}
}public class Language {public static void sayHi() {System.out.println("Hi,Im Language");}
}
MDove:说白了就是:老子是老子的,儿子是儿子的。其实这个也比较好理解。static的变量、方法都是伴随类存在的,类加载完毕就生成了。它和对象new不new是没有关系的。因此也不存在什么实际变量一说。因此也就有了上边的这种情况,也就是常说的:隐藏父类。
小A:这些内容,学习的时候还真没有好好的去思考...以后要加油了!
剧终
转载于:https://blog.51cto.com/13931046/2171493
相关文章:

一笔画问题【数据结构-图论】
回家路上听到2个人在说:田字怎么一笔写成,并且笔划不重复。 田 我回家想了许久,觉得无论如何走正常的途径肯定是不行的,投机取巧脑筋急转弯的我不讨论。 那么是否可以找到数学定理? 其实就是欧拉七桥问题:1…

解析并符号 读取dll_Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别。本篇就以BeanFactory基础容器接口的默认实现类XmlBeanFactory启动流程分析来入门Spring源码的学习。二. 概念要点1. 概念定义Be…

安装多个gcc
删除gcc #yum remove gcc 安装最新的 #yum install gcc 查找gcc源 可先通过“yum list compat-gcc*”查看版本,然后再利用“yum install compat-gccXXX”安装 #yum list compat-gcc* #sudo yum install compat-gcc-34.i686 查看gcc版本 #gcc -v #gcc34 -v参考&…

JAVA - HashMap和HashTable
1. HashMap 1) hashmap的数据结构 Hashmap本质就是一个数组,只是当key值重复时,使用链表的方式来存储重复的key值(拉链法),注意:链表中存放的仍然是key值。如下图示: 当我们往hashmap中put元素…

empress和queen区别_queen与empress
(1)「queen」和「empress」不仅可以用来指称「king」和「emperor」的妻子,也能指代握有正式权力的女性君主。(2) 英国的君主(queen或king)之所以曾经有过empress或emperor的头衔,是因为英国曾统治过印度次大陆, 他们曾是印度的empress或emperor。如今&am…

在UIWindow上加类似于“回到顶部”的按钮
在公司上个版本的开发中遇到了一个UI布局的小问题: 某个页面需要增加一个分享按钮,但是该页面是二级页面,导航栏右边也已经放置了2个button。 起初和老大谈论这个问题的时候想到的方法是导航栏右边加三个button得了~但是一回想,这…

修改mysql 默认字符集 , 默认引擎
cd /var/lib/mysql/gamell vim db.optdefault-character-setutf8default-collationutf8_general_ciwq service mysqld restart或者service mysqld reload默认字符集修改完毕vim /etc/my.cnf[mysqld]datadir/var/lib/mysqlsocket/var/lib/mysql/mysql.sockusermysqldefault-stor…

bzoj2724: [Violet 6]蒲公英(分块)
传送门 md调了一个晚上最后发现竟然是空间开小了……明明算出来够的…… 讲真其实我以前不太瞧得起分块,觉得这种基于暴力的数据结构一点美感都没有。然而今天做了这道分块的题才发现分块的暴力之美(如果我空间没有开小就更美了) 我们先将整个…

Linux0.01内核根目录Makefile注释
# # Makefile for linux. # If you dont have -mstring-insns in your gcc (and nobody but me has :-) # remove them from the CFLAGS defines. ## #8086汇编编译器和连接器. -0生成8086目标程序;-a生成与gas和gld部分兼容的代码 # AS86 as -0 -a CC86 cc -0 LD86 ld -0# #G…

快速滚动_方老师教滚动快速作文
五年级第一单元作文集阴沉天空中有一小束照着你的阳光。亲爱的孩子,让时间在知识的枝条上、智慧的绿叶上、成熟的果实上留下它勤奋的印痕!罗婉汀作文集自律且努力,别让生活太安逸。亲爱的孩子,耕耘者最信得过自己的汗水࿰…

liunx复制备份命令,copy命令,liunx命令
2019独角兽企业重金招聘Python工程师标准>>> 拷贝到的文件夹 /usr/local/文件夹/需要拷贝的路径文件夹 /usr/local/tomcat/webapps/文件夹/复制命令cp -r /usr/local/文件夹/ /usr/local/tomcat/webapps/文件夹/ 转载于:https://my.oschina.net/u/2336787/blog/635…

20.Valid Parentheses (python)
这道题主要用栈来实现的。什么是栈呢,参照书上的后缀表达式的例子谈谈自己的理解,栈最明显的特征是先进后出。所以可以有效的结合题目中 ()对匹配问题,可以把从列表中获取的符号先存到栈中。 首先建个空列表用于映射栈…

The HipHop Virtual Machine
目前Facebook已将该HipHop虚拟机开源,源代码发布在GitHub上。关于该工具的技术原理在Facebook的开发者页面上有一篇详细的文章介绍,查看这里。如果看不到的可以看下面的转载:Were always looking for ways to make our computing infrastruct…

node建立博客系统遇到的问题,1,乱码。2,multer的使用错误。3使用session问题...
2019独角兽企业重金招聘Python工程师标准>>> 1,乱码 文件存储为utf-8格式后还是报错。 原来这个设置只对新建文件编码有效,旧文件不处理的,我还以为旧文件也给转换了。 2,上传文件的multer模块使用错误。 throw new Ty…

python时间函数入门_calendar在python3时间中有哪些常用函数?怎么用?
想要在python中写代码游刃有余,没有函数的支持是万万不行的。很多小伙伴反映,最近函数的应用知识不够了,所以小编挑选了python3时间中的函数,希望可以帮助大家在处理日历方面更加的迅速。其他更多的函数,大家也可以自行…

9.spark core之共享变量
简介 spark执行操作时,可以使用驱动器程序Driver中定义的变量,但有时这种默认的使用方式却并不理想。 集群中运行的每个任务都会连接驱动器获取变量。如果获取的变量比较大,执行效率会非常低下。每个任务都会得到这些变量的一份新的副本&…

【CSDN2012年度博客之星】需要您的一票,感谢大家的支持
从2004年9月,本人一直将自己工作和学习经验写成博文分享给大家,本人有幸被选为2012年88位候选博客之星,如果各位IT‘er喜欢我的博文,请投我一票,做…

双绞线和同轴电缆
线缆作为连接器件,相当于不同系统之间沟通的“桥梁”,选择线缆类型的好坏,也决定着传输信号的质量,影响着整个系统的稳定性。 (1)特性阻抗 先说一下关于线缆在传输过程中的特性阻抗问题。 特性阻抗是指电缆…

keras训练完以后怎么预测_使用Keras建立Wide Deep神经网络,通过描述预测葡萄酒价格...
你能通过“优雅的单宁香”、“成熟的黑醋栗香气”或“浓郁的酒香”这样的描述,预测葡萄酒的价格吗?事实证明,机器学习模型可以。在这篇文章中,我将解释我是如何利用Keras(tf.keras)建立一个Wide & Deep神经网络,并…
如何发布自己的NPM包(模块)?
1.注册NPM 账号 注册地址:https://www.npmjs.com/。 2.初始化自己要发布的项目 搭建本地环境:安装node.js,包含了npm命令。新建目录,在该目录下,初始化项目:npm init。按照提示填写初始化信息,我…

PHP对于浮点型的数据需要用不同的方法去解决
Php: BCMathbc是Binary Calculator的缩写。bc*函数的参数都是操作数加上一个可选的 [int scale],比如string bcadd(string $left_operand, string $right_operand[, int $scale]),如果scale没有提供,就用bcscale的缺省值。这里大数直接用一个…

mysql提示符详解_MySQL字符集使用详解
查看字符集相关变量mysql> show variables like character%;————————–——————————-| Variable_name | Value |————————–——————————-| character_set_client | latin1 || character_set_connection | latin1 || character_set_database…

Apache漏洞修复
今天受同事的委托,修复一台服务器的Apache漏洞,主要集中在以下几点: 1.Apache httpd remote denial of service(中危) 修复建议:将Apache HTTP Sever升级到2.2.20或更高版本。 解决方法:升级HTT…

Java遍历Map对象的四种方式
关于java中遍历map具体哪四种方式,请看下文详解吧。 方式一 这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。 1 2 3 4 Map<Integer, Integer> map new HashMap<Integer, Integer>(); for (Map.Entry<Integer, Intege…

Tokyo Cabinet 安装
tokyocabinet :一个key-value的DBM数据库,但是没有提供网络接口,以下称TC。 tokyotyrant :是为TC写的网络接口,他支持memcache协议,也可以通过HTTP操作,以下称TT。 Tokyo Cabinet 是一款 DBM 数据库,Tokyo …

Packagist / Composer 中国全量镜像
Packagist 镜像 请各位使用本镜像的同学注意: 本镜像已经依照 composer 官方的数据源安全策略完全升级并支持 https 协议!请各位同学 按照下面所示的两个方法将 http://packagist.phpcomposer.com 修改为 https://packagist.phpcomposer.com 还没安装 co…
centos yum mysql-devel 5.5_CentOS 6.5下yum安装 MySQL-5.5全过程图文教程
在linux安装mysql是一个困难的事情,yum安装一般是安装的mysql5.1,现在经过自己不懈努力终于能用yum安装mysql5.5了。下面通过两种方法给大家介绍CentOS 6.5下yum安装 MySQL-5.5全过程,一起学习吧。方法一:具体方法和步骤如下所示&…

py 的 第 31 天
1.osi 7层模型 5层: 应用层 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 4层: 应用层 应用层 表示层 会话层 传输层 网络层 物理层 数据链路层 物理层 注意:7层背会。 2.tcp的三次握手&…

mysql实训报告_mysql数据库技术》实验报告.doc
mysql数据库技术》实验报告MySQL数据库技术实验报告系 别班 级学 号姓 名地点地点机房课程名称MySQL数据库技术实验名称实验1 MySQL的使用实 验 过 程目的要求:(1)掌握MySQL服务器安装方法(2)掌握MySQL Administrator的基本使用方法(3)基本了解数据库及其对象实验准…

PHP中魔术方法的用法
PHP中魔术方法的用法 /** PHP把所有以__(两个下划线)开头的类方法当成魔术方法。所以你定义自己的类方法时,不要以 __为前缀。 * */// __toString、__set、__get__isset()、__unset() /*The __toString method allows a class to decide how …