[短文速读] 重载有暗坑,重载重写你真的了解么

栏目: Java · 发布时间: 6年前

内容简介:这将是一个系列文章。原因是自己写了很多文章,也看了很多文章。从最开始的仅仅充当学习笔记,到现在认认真真去写文章去分享。中间发现了很多事情,其中最大发现是:收藏不看!总是想着先收藏以后有时间再看,到后来…大家都懂得。大多数文章仿佛石沉大海,失去了应有的价值。因为技术文章大多需要比较重的思考,但是现如今时间碎片化很严重,因此收藏不看也实属不得已。所以萌生了这个系列的想法,系列文章的特点:以一些日常开发中不起眼的基础知识点为内核,围绕此包裹通俗易懂的文字。尽量用少思考的模式去讲述一个知识。让我们能够真正在碎片化

前言

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

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

出场角色

小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:这些内容,学习的时候还真没有好好的去思考...以后要加油了!

剧终


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ethnography and Virtual Worlds

Ethnography and Virtual Worlds

Tom Boellstorff、Bonnie Nardi、Celia Pearce、T. L. Taylor / Princeton University Press / 2012-9-16 / GBP 21.00

"Ethnography and Virtual Worlds" is the only book of its kind - a concise, comprehensive, and practical guide for students, teachers, designers, and scholars interested in using ethnographic methods t......一起来看看 《Ethnography and Virtual Worlds》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试