java多态只能用在类似于 方法(父类) 吗?而 父类 = new 子类 的意义是什么?

多态最大的作用就是为了传参提供便利,但我们不应该只看到这一层,还要往下再走走:为什么要用父类引用指向子类实例呢?

就好比你看到一把刀很锋利,可以切菜,你不应该疑惑“难道刀就是拿来切菜的吗”,而应该关注“为什么刀可以如此锋利”...

回到你的问题上来,我们更应该关心:为什么可以使用多态机制,以及为什么需要多态?

多态怎么实现的?

我并非计算机专业,所以对于这个问题,只给出一个大概的解释。多态从语法表面上看,就是子类对象可以赋值给父类引用,并且通过该引用可以动态地调用不同子类的方法。

多态按实际用法又可以分为:

  • 继承多态
  • 接口多态

所谓继承多态(吸烟有害健康,我不抽烟):

class Son extends Father {
    @Overrid
    public void smoke() {
        System.out.print("儿子抽烟");
    }
}
class Daughter extends Father {
    @Overrid
    public void smoke() {
        System.out.print("女儿抽烟");
    }
}

// 继承多态,因为Son、Daughter继承了Father
Father obj = new Son();
obj.smoke(); // 打印:儿子抽烟
obj = new Daughter();
obj.smoke(); // 打印:女儿抽烟

所谓接口多态:

class Son implements Swimmer {
    @Overrid
    public void swim() {
        System.out.print("儿子游泳");
    }
}
class Daughter implements Swimmer {
    @Overrid
    public void swim() {
        System.out.print("儿子游泳");
    }
}

// 接口多态,因为Son、Daughter实现了Swimmer
Swimmer obj = new Son();
obj.swim(); // 打印:儿子游泳
obj = new Daughter();
obj.swim(); // 打印:女儿游泳

实际开发接口多态更常用。

多态的实现,依赖于2个大方面:

  • 机制上的支持
  • 编码上的支持

机制支持

首先,编译器要允许这种赋值方式,不然把son赋值给swimmer就会像把 int a赋值给String b一样报错。

其次,运行时要支持并且能通过某种机制找到真正的子类方法。

编码支持

必须存在继承(实现)关系 + 子类必须重写(实现)父类的方法

我们一般所说的多态,其实都是指方法的多态

什么意思呢?以上面Swimmer的代码为例(假设整个工程只有这么几个类),当程序运行时,JVM中实际上并不存在一个对象叫Swimmer,自始至终只有Son和Daughter两个对象,而且Son和Daughter都实现了Swimmer,且重写了swim()方法。当JVM运行到:

image.png

JVM是怎么知道要打印“儿子游泳”的呢?换句话说,JVM怎么知道调用Son#swim()而不是Swimmer#swim()或者Daughter#swim()的呢?


image.png

这就涉及到所谓的“[虚方法]”和“虚方法表”了。JVM的知识点大概如下:


image.png

在类加载过程中,有loading、linking、initialization三个阶段,其中linking(链接)阶段又包括3个小阶段:

  • verify(验证)
  • prepare(准备)
  • resolve(解析)

其中在resolve阶段,JVM会针对类或接口、字段、类方法、接口方法等进行相应解析,其中方法信息会形成所谓的“虚方法表”。

image.png

也就是说,当出现多态方法调用时,底层会多一次“查表”的过程,也就是通过搜索虚方法表,确定本次实际应该调用的方法(实际指向对象+实例对应的类有无重写父类方法),如果子类Override了父类方法,那么就会执行子类方法。

多态与设计模式

很多初学编程的人,一定会记住两句话,即使他们并不懂得其中含义:

  • 面向对象的三大特性是:封装、继承、多态
  • 万物皆对象

但在我眼里,封装、继承这俩货和多态根本不是一个档次的(就好比李云迪和郎朗),多态才是面向对象的核心和根本,甚至没有多态就没有面向对象。举个例子,C语言没有封装吗?不也是可以抽取方法吗?也有结构体呢,看起来不像对象吗?再者,你问问自己,你使用继承是为了什么?不就是为了贪图父类的那一点点已经写好的方法,为了偷点懒吗?既然是为了少写一点代码,我抽取成方法不行吗?

所以,到底什么是面向对象呢?

这就回到了我上面说的,多态才是面向对象的核心(当然,面向对象本质是一种编程思想的转变)。当我们有了多态,才能写出更加抽象的代码,而抽象代表稳定

假设世界末日,外星人占领地球了,它们觉得必须杀鸡儆猴,我们因为真的打不过,只能任由宰割。此时我们签订契约:你们可以杀一个动物。于是我们送了一只实验室的小白鼠,因为小白鼠也是动物呀。动物这个词是抽象的,后面我们送啥都可以,只要不送人。

再举个编程的例子。假设在写好的一个类文件中,你写下这样一段代码:


image.png

如果后期接入拼多多,你就需要修改代码。但如果使用策略模式,就可以用增量的方式代替修改(开闭原则):

image.png
image.png

具体可以参考:优化代码中大量的if/else,你有什么方案?

没错,这就是策略模式。而所谓的设计模式,其实有一本书的书名,恰恰点破了设计模式的本质:

image.png

是的,设计模式本质是围绕着“在面向对象的基础上,如何复用设计”这个原则展开的...所以本质又回到了面向对象。

为什么设计模式这么牛逼,能把很多看起来像“屎山一样”的代码优化得清晰、简洁?本质上就是多态!

所谓“屎山一样”的代码,大概率就是因为后期需求不断迭代,开发人员在未经思考的情况下肆意使用if else添加逻辑分支导致的!但分支是不会无缘无故消失的,只是借助设计模式把分支下推,最终交给了多态——JVM,你给我去查虚方法表。

换句话说就是:JVM,这坨屎你来吃。

最终,JVM带着虚方法表承受了一切,而我们的上层代码一扫阴霾,看起来干净而整洁,也就是所谓的clean code...

所以,最后再问一句:多态真的就是用来传参吗?

你可能感兴趣的:(java多态只能用在类似于 方法(父类) 吗?而 父类 = new 子类 的意义是什么?)