博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[短文速读] 重载有暗坑,重载重写你真的了解么
阅读量:6378 次
发布时间:2019-06-23

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

前言

这将是一个系列文章。原因是自己写了很多文章,也看了很多文章。从最开始的仅仅充当学习笔记,到现在认认真真去写文章去分享。中间发现了很多事情,其中最大发现是:收藏不看!总是想着先收藏以后有时间再看,到后来…大家都懂得。大多数文章仿佛石沉大海,失去了应有的价值。

因为技术文章大多需要比较重的思考,但是现如今时间碎片化很严重,因此收藏不看也实属不得已。所以萌生了这个系列的想法,系列文章的特点:以一些日常开发中不起眼的基础知识点为内核,围绕此包裹通俗易懂的文字。尽量用少思考的模式去讲述一个知识。让我们能够真正在碎片化的时间里学到东西!

出场角色

小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,重写的话,这个结论是不成立的!

public class MethodMain {    public static void main(String[] args) {        Language language = new MethodMain().new Java();        language.sayHi();    }    public class Java extends Language {        @Override        public 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:没错,毕竟重写涉及到你是调用子类的方法还是调用父类。因此需要在运行期间去决定。当然我们用嘴说是很轻巧的,实际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/9970791/2170956

你可能感兴趣的文章
Java Outputstream to String
查看>>
RS232C串口通信接线方法(三线制)
查看>>
Android 自定义View属性相关细节
查看>>
type already defined error in Eclipse
查看>>
OSA 安装
查看>>
先安装.Framework然后再安装IIS,ASP.NET程序不能运行
查看>>
NPOI Excel下拉项生成设置
查看>>
360该不该拍?
查看>>
用Xib创建控制器
查看>>
oracle的sqlplus和dos的中文乱码问题
查看>>
LVS+keepalived高可用负载均衡集群部署(二)---LAMP网站服务器与LVS服务器
查看>>
Struts2之简单数据类型转换
查看>>
python 打印数字
查看>>
iptables规则的查看、添加、删除和修改
查看>>
打开网站显示输入用户名和密码
查看>>
size_t的32位和64位兼容
查看>>
HBase全分布式模式的安装和配置
查看>>
Spring 框架的设计理念与设计模式分析
查看>>
十年web老兵整理的前端视频资料
查看>>
CentOS 6.3 上安装 Oracle 11g R2(转)
查看>>