你我皆风华正茂,梦死方坠人生暮年

1.什么是多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用变量调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

2.多态的好处

  • 使代码间的耦合度降低

  • 减少代码冗余,提高可扩展能力

3.多态分类

多态分为编译时多态和运行时多态:

  • 编译时多态主要指方法的重载。

  • 运行时多态指方法重写,程序中定义的对象引用所指向的具体类型在运行期间才确定。

我们所说的多态一般都指运行时多态,运行时多态有三个条件:

  • 继承:在多态中必须存在有继承关系的子类和父类。

  • 覆盖(重写):子类对父类中某些方法进行重新定义,使得父类与子类对同一行为表现不同。

  • 向上转型:在多态中需要将子类的引用赋给父类对象,这样不同子类对同一行为的不同表现。

4.向上转型与向下转型

向上转型(自动转型):子类对象的引用赋值给父类类型的引用,即父类引用指向子类的实例对象。也可以将接口类型的引用指向接口实现类的实例对象。

注意:

  • 向上转型会导致子类的特有方法不可用。

向下转型(强制类型转化):将父类类型的引用强制转换成子类类型的引用。

  • 注意:

    • 向下转型的前提是父类类型的引用本来就指向要转化类型的实例对象,否则会报ClassCastException异常,也就是说向下转型的之前,他得向上转型。

    • 向下转型只能转型为本类对象,向下转型之前最好用instanceof判断一下。

    • 向下转型后可以使用子类的特有方法了。

public class TypeChangeTest {    public static void main(String[] args) {        Animal dog = new Dog();        dog.eat();        //dog.speak(); //Cannot resolve method 'speak()' 不能调用子类特有方法        ((Dog) dog).speak();        //((Cat) dog).speak(); //ClassCastException        CanFly bird = new Bird();        bird.fly();        //bird.speak(); //Cannot resolve method 'speak()'        ((Bird) bird).speak();    }}class Animal{    public  void eat() {        System.out.println("Animal:eat()");    }}class Dog extends Animal{    @Override    public  void eat() {        System.out.println("Dog:eat()");    }    public void speak() {        System.out.println("Dog:speak()");    }}class Cat extends Animal{    @Override    public  void eat() {        System.out.println("Cat:eat()");    }    public void speak() {        System.out.println("Cat:speak()");    }}interface CanFly{    public abstract void fly();}class Bird implements CanFly {    @Override    public void fly() {        System.out.println("Bird:fly()");    }    public void speak() {        System.out.println("Bird:speak()");    }}

5.方法重载(Overload)(重点)

方法重载就是在同一个类中,出现了多个同名方法,他们的参数列表不同(参数列表的个数不同、参数列表的数据类型不同、参数列表的顺序不同)。

5.1 方法重载的三种方式

(1)参数列表的个数不同

int add(int a, int b)int add(int a, int b, int c)

(2)参数列表的数据类型不同

int add(int a, int b)int add(int a, float b)

(3)参数列表的顺序

int add(int a, float b)int add(float a, int b)
5.2 方法重载的特点
  • 方法重载与返回值类型、与访问权限无关。

  • 参数列表不同指的是参数列表的个数不同、参数列表的数据类型不同或者参数列表的顺序不同,若仅仅是参数的名称不同,不叫重载。

根据实际参数的数据类型、个数、顺序,编译器在编译时就能够确定执行那个重载方法了,因此编译时多态指的就是方法重载。

5.3 重载的实现原理:静态分派

在说明静态分派之前,需要知道:

  • 变量的静态类型:引用类型,不会变化,编译期就能确定。

  • 变量的动态类型:实例对象的类型,会变化,运行期才能确定。

举个例子:

Human man = new Man();man = new Woman();

变量的静态类型为Human,动态类型可以是Man,也可以是Woman。静态类型可以通过强制类型转换来改变:

// 强制类型转换Woman man = (Woman)man;// 此时man的静态类型从 Human 变为 Woman

静态分配就是依赖类型来决定方法执行的版本,发生在编译期,所以不由 Java 虚拟机来执行,最典型的应用就是重载。

public class StaticDispatch {    static abstract class Human {    }    static class Man extends Human {    }    static class Woman extends Human {    }    public void sayHello(Human human) {        System.out.println("hello,guy!");    }    public void sayHello(Man man) {        System.out.println("hello,gentleman!");    }    public void sayHello(Woman woman) {        System.out.println("hello,lady!");    }    // 测试代码    public static void main(String[] args) {        Human man = new Man();        Human woman = new Woman();        StaticDispatch sr = new StaticDispatch();        sr.sayHello(man);        sr.sayHello(woman);        sr.sayHello((Woman) woman);    }}
输出:hello,guy!hello,guy!hello,lady!

解析:
StaticDispatch 有三个sayHello重载方法,由于重载是通过静态分派实现的,也就是说具体选择执行那个重载方法取决于man和woman的静态类型,而他们的静态类型都是Human,所以执行的是参数为Human的方法,这在编译时就已经确定下来了。而当我们通过强制类型转换,将woman的静态类型改为Woman时,就会执行参数Woman的重载方法。

5.4 静态分派中的匹配优先级

当需要进行静态分配而程序中没有指定的静态类型重载方法时,程序就会根据静态类型的优先级,选择一个“相对更合适的”重载版本执行。

静态分派中的匹配优先级:直接匹配类型>自动类型转换>自动装箱>匹配接口>匹配父类(根据继承关系子类>父类)>匹配变长参数

例如:

public class OverloadTest {    public double add(double a, double b) {        return a + b;    }    public float add(Float a, Float b) {        return a + b + 1;    }    public void test(Float a) {        System.out.println("this is Double");    }    public void test(Object a) {        System.out.println("this is Object");    }    public static void main(String[] args) {        OverloadTest ot = new OverloadTest();        float num1 = 1.1f, num2 = 2.2f;        System.out.println(ot.add(num1, num2));// 3.3000000715255737        ot.test(num1); // this is Double    }}

解析:
由于add没有直接匹配相应的重载方法,但是add有float的自动类型转换的重载方法和float包装类的重载方法,而自动类型转换优先级高于包装类,所以执行了add(double a, double b);同理test重载方法中包装类型是Object子类,优先调用test(Float a)。

Tip:

  • 同一优先级编译器无法自动转型,因此会报出编译时期异常。

6.方法重写(Override)(重点)

在子类中声明已存在与父类中的方法称为重写。重写的好处在于子类可以根据需要,定义特定于自己的行为。方法重写需要满足里式替换原则。

6.1 方法重写的特点
  • 参数列表必须完全与被重写方法的相同,返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类。

  • 不能被重写的方法:构造方法、final方法、没有访问权限的方法(如private方法)

  • 访问权限不能比父类中被重写的方法的访问权限更低。

  • 重写的方法能够抛出任何非受检查异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的受检查异常,或者比被重写方法声明的更广泛的受检查异常,反之则可以。

class SuperClass {      protected Listfunc() throws Throwable {            return new ArrayList<>();   }}class SubClass extends SuperClass {        @Override     public ArrayList func() throws Exception {            return new ArrayList<>();    }}

Tips:

  • 使用@Override注解可以检查重写的正确性。

  • 除RuntimeException和Error外,都是受检查异常。了解关于异常更多的细节:Java基础深度总结:异常

  • 静态方法不能重写,可以被再次声明。

6.2 重写实现原理:动态分派

动态分派就是根据变量的动态类型进行方法分派的行为,即根据变量的动态类型确定执行那个方法。

// 定义类class Human {    protected void sayHello() {        System.out.println("Human say hello");    }}// 继承自 抽象类Human 并 重写sayHello()class Man extends Human {    @Override    protected void sayHello() {        System.out.println("man say hello");    }}class Woman extends Human {    @Override    protected void sayHello() {        System.out.println("woman say hello");    }}public class DynamicDispatch {    // 测试代码    public static void main(String[] args) {        Human man = new Man();        man.sayHello();        man = new Woman();        man.sayHello();    }}
输出:man say hellowoman say hello

解析:
Human man = new Man(); man的动态类型是Man,因此执行Man类中重写的sayHello方法 ;man = new Woman(); man的动态类型变为Woman,因此执行Woman中重写的sayHello方法。

6.4 动态分派中的匹配优先级

在调用一个方法时,先从本类中查找看是否有对应的方法,如果没有再到父类中查看,看是否从父类继承来。否则就要对参数进行转型,转成父类之后看是否有对应的方法。总的来说,方法调用的优先级为:this.func(this) > super.func(this) > this.func(super) > super.func(super)

/*    A    |    B    |    C    |    D */class A {    public void show(A obj) {        System.out.println("A.show(A)");    }    public void show(C obj) {        System.out.println("A.show(C)");    }}class B extends A {    @Override    public void show(A obj) {        System.out.println("B.show(A)");    }}class C extends B {}class D extends C {}public class OverridePriorityTest {    public static void main(String[] args) {        A a = new A();        B b = new B();        C c = new C();        D d = new D();        // 在 A 中存在 show(A obj),直接调用        a.show(a); // A.show(A)        // 在 A 中不存在 show(B obj),将 B 转型成其父类 A        a.show(b); // A.show(A)        // 在 B 中存在从 A 继承来的 show(C obj),直接调用        b.show(c); // A.show(C)        // 在 B 中不存在 show(D obj),但是存在从 A 继承来的 show(C obj),将 D 转型成其父类 C        b.show(d); // A.show(C)        // ba 的动态类型为 B ,所以 ba 和 b 的调用结果一样        A ba = new B();        ba.show(c); // A.show(C)        ba.show(d); // A.show(C)    }}

7.方法重载与方法重写对比

8.静态方法和字段不参与多态

静态方法和属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。
所以访问属性(字段)和静态方法只看声明。

java 多态_Java基础深度总结:多态相关推荐

  1. java中的多态_Java中的多态

    多态与HoFs 朋友们好久不见啊,最近笔者同时在写脚本型语言--JavaScript,和工业级的面向对象语言--Java. 在写代码的同时呢,也会思考这些语言的不同.今天就拿 Java 中的多态,来谈 ...

  2. java继承与多态_Java继承与多态

    感慨一下,到了现在感觉Java里面很多东西都是模模糊糊,不能这样了,一点点解决吧.今天看了继承与多态的一些内容,感觉看得很浅,先写下来,算是巩固,如果后面看到更好的内容,再慢慢加上去. 继承与多态,他 ...

  3. java实验报告4继承与多态_Java继承与多态实验报告

    西 西 安 安 邮 邮 电 大 学 (计算机学院) 课内实验报告 实验名称: : 态 继承与多态 ﻩ ﻩ 专业名称: 计算机科学与技术 班 班 级: 计科 1405 班 学生姓名: 高宏伟 学 学 号 ...

  4. java乐器_java类的多态编程。 (1)乐器(Instrument)分为:钢琴(Piano)、小提琴(Vio...

    展开全部 public class Instrument { public void play(){ System.out.println("演奏乐器......"); } } p ...

  5. java继承和多态的实验报告_JAVA,继承和多态实验报告

    实验项目名称 : 继承和多态 ( 所属课程 : Java 语言程序设计 ) 院 系: 专业班级: 姓 名: 学号: 实验地点: 指导老师: 本实验项目成绩: 教师签字: 日期: 1.实验目的 (1)掌 ...

  6. java 多态_Java 多态

    Java多态,在之前的向上转型的文章中已经介绍了使用场景和为什么要通过向上转型实现运行时多态,请先看文章:张舰:Java 向上转型和向下转型 这篇文章主要是总结一下Java多态的概念,具体的例子请参考 ...

  7. java 多态_Java面向对象 —— 多态

    前两天已经相继介绍了Java面向对象的三大特性之中的封装.继承,所以今天就介绍Java面向对象的三大特性的最后一项,多态~ 首先讲一下什么是多态,以及多态需要注意的细节 什么是多态:一个对象具备多种形 ...

  8. [转载] c++多态与java多态性_Java中的多态性

    参考链接: Java中的加法和串联 c++多态与java多态性 Polymorphism is one of the core concepts of OOPS paradigm. The meani ...

  9. java面向对象的多态_java面向对象(五)之多态

    多态 面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法. ...

最新文章

  1. FaceBook开源PyTorch3D:基于PyTorch的新3D计算机视觉库
  2. mysql variables_通过什么命令能够改变mysql的variables的变量里的值?
  3. 组态王中时间存access怎么存,组态王通过Access数据库起始截止日期查询方法
  4. Sparkmllib scala线性回归
  5. 《Linux内核设计与实现》读书笔记(十六)- 页高速缓存和页回写
  6. ActiveReports 报表应用教程 (4)---分栏报表
  7. python写入数据的一种措施_Python 文件数据读写的具体实现
  8. 自动设置图片的序号_编写学位论文时如何给表格和图片自动编号
  9. python编程(类变量和实例变量)
  10. 网奇iwms插件之“我浏览过的文章”
  11. sonar-runner命令模式运行sonar
  12. Visio 安装与操作小结
  13. java使用Redis实现点赞功能
  14. html导航栏固定在顶部,将导航栏始终固定在窗口顶部:
  15. scrapy_redis分布式爬虫遇到的问题DEBUG: Filtered offsite request to
  16. Golang Hotfix技术背景
  17. VBox 快照备份虚拟机
  18. 阿里云盘和 Teambition 网盘
  19. 算法【动态规划】 | 【01】二维表结构
  20. Windows10如何配置java环境

热门文章

  1. 抢那么多封面,有那么多钱发红包吗?
  2. 释放内容化势能 聚划算《划算8点档》给出新思路
  3. 上海美特斯邦威成被执行人 执行标的超79万
  4. 工信部下架37款侵害用户权益APP 114票务网等在列
  5. 苏宁易购第二次债券购回基本方案:购回资金总额20亿元
  6. 给还是不给?又一个国家要求苹果必须为iPhone 12提供充电器
  7. 没理由不买它!小米今年最后一款旗舰发布:性价比真的高
  8. 刘慈欣、Netflix联手!《三体》系列将拍摄剧集,但编剧被网友疯狂吐槽
  9. 格力回应被中国移动取消中标资格:系投标人员整理材料失误
  10. 下半年登场!小米MIX 4概念图曝光:有望首发屏下摄像头