【JavaSE】面向对象编程思想之多态(图文详解)

这篇具有很好参考价值的文章主要介绍了【JavaSE】面向对象编程思想之多态(图文详解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

目录

1. 多态的概念

2. 多态实现条件

3. 重写

4. 向上转型和向下转型

4.1 向上转型

4.2 向下转型

5. 多态的优缺点

6. 避免在构造方法中调用重写的方法


1. 多态的概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 总的来说:同一件事情,发生在不同对象身上,就会产生不同的结果。

2. 多态实现条件

在java中要实现多态,必须要满足如下几个条件,缺一不可:

1. 必须在继承体系下

2. 子类必须要对父类中方法进行重写

3. 通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。 

下面是一个多态的示例:

class Animal {
    public void makeSound() {
        System.out.println("动物发出声音");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("猫发出喵喵的声音");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("狗发出汪汪的声音");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Cat();
        Animal animal2 = new Dog();

        animal1.makeSound(); // 输出:猫发出喵喵的声音
        animal2.makeSound(); // 输出:狗发出汪汪的声音
    }
}

3. 重写

引入:

在阅读下面代码之前,建议先学习向上转型。

class Animal {
    String name;
    int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(name + "吃饭");
    }
}
class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
}
public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog("小狗",2);
        animal.eat();
    }
}

 对于上面的代码,我们可以轻松的看出程序将会打印的内容:

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 但是,如果在Dog这个类中也有eat()这个方法,程序会输出什么样的结果呢:

class Animal {
    String name;
    int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(name + "吃饭");
    }
}
class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    public void eat() {
        System.out.println(name + "吃骨头~~");
    }
}
public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog("小狗", 2);
        animal.eat();
    }
}

这时我们发现,程序输出结果:

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 此时的你是不是也在想,为什么输出的不是父类的eat方法呢?这里就要讲到重写了:

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

以上面代码为例:【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 此时通过父类的引用调用的是子类的eat()方法,我们把这个过程称为动态绑定。

【方法重写的规则】

  1. 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型方法名 (参数列表) 要完全一致
  2. 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
  3. 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected.访问权限:private < default < protected <public
  4. 父类被static、private修饰的方法、构造方法都不能被重写。
  5. 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.

对于第3种情况的代码示意:

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 文章来源地址https://www.toymoban.com/news/detail-628885.html

 【重写和重载的区别】

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 即:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 

【重写的设计原则】

对于已经投入使用的类,尽量不要进行修改。最好的方式是:重新定义一个新的类,来重复利用其中共性的内容, 并且添加或者改动新的内容。

例如:若干年前的手机,只能打电话,发短信,来电显示只能显示号码,而今天的手机在来电显示的时候,不仅仅可以显示号码,还可以显示头像,地区等。在这个过程当中,我们不应该在原来老的类上进行修改,因为原来的类,可能还在有用户使用,正确做法是:新建一个新手机的类,对来电显示这个方法重写就好了,这样就达到了我们当今的需求了。

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。

动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。

4. 向上转型和向下转型

4.1 向上转型

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。

语法格式父类类型 对象名 = new 子类类型()

举个“栗”子:

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。

 【使用场景】

1. 直接赋值

直接赋值:子类对象赋值给父类对象

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

2. 方法传参

方法传参:形参为父类型引用,可以接收任意子类的对象

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

3. 方法返回

作返回值:返回任意子类对象

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

向上转型的优点:让代码实现更简单灵活。

向上转型的缺陷:不能调用到子类特有的方法。

4.2 向下转型

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 

class Animal {
    String name;
    int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "吃饭");
    }
}

class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name + "吃鱼~~~");
    }

    public void mew() {
        System.out.println(name + "喵喵叫");
    }
}


class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name + "吃骨头~~~");
    }

    public void bark() {
        System.out.println(name + "汪汪叫");
    }
}

class Test {
    public static void main(String[] args) {
        Animal animal = new Dog("小狗", 1);
        Dog dog = (Dog) animal; //向下转型
        dog.bark();
    }
}

上面的代码时可以成功编译的:

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

但是,当main方法写成下面这样时:

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

此时编译器并没有错误提示,但当程序运行的时候就出错了:

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

在上面的代码中,存在一个类型转换错误。在Test类的main方法中,尝试将一个Animal对象转换为Cat对象,这是不正确的,因为animal实际上是一个Dog对象,无法强制转换为Cat对象。

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入 了 instanceof ,如果该表达式为true,则可以安全转换。

class Test {
    public static void main(String[] args) {
        Animal animal = new Dog("小狗", 1);
        if(animal instanceof Cat){
            Cat cat = (Cat)animal;
            cat.mew();
        }else {
            System.out.println("dog 无法转化为 cat");
        }
    }
}

输出结果: 

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 

5. 多态的优缺点

假设有如下代码:

class Shape {
    //属性....
    public void draw() {
        System.out.println("画图形!");
    }
}

class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("♦");
    }
}

class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("●");
    }
}

class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

【使用多态的好处】

1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else

什么叫 "圈复杂度" ? 圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂. 因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 "圈复杂度". 如果一个方法的圈复杂度太高, 就需要考虑重构. 不同公司对于代码的圈复杂度的规范不一样. 一般不会超过10。

例如我们现在需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下:

public static void drawShapes() {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Flower flower = new Flower();
        String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
        for (String shape : shapes) {
            if (shape.equals("cycle")) {
                cycle.draw();
            } else if (shape.equals("rect")) {
                rect.draw();
            } else if (shape.equals("flower")) {
                flower.draw();
            }
        }
    }

如果使用使用多态, 则不必写这么多的 if - else 分支语句, 代码更简单.

public static void drawShapes() {
        // 我们创建了一个 Shape 对象的数组.
        Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),
                new Rect(), new Flower()};
        for (Shape shape : shapes) {
            shape.draw();
        }
    }

2. 可扩展能力更强

如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低.

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("△");
    }
}

对于类的调用者来说(drawShapes方法), 只要创建一个新类的实例就可以了, 改动成本很低. 而对于不用多态的情况, 就要把 drawShapes 中的 if - else 进行一定的修改, 改动成本更高.

多态缺陷:代码的运行效率降低。

1. 属性没有多态性

        当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

2. 构造方法没有多态性

6. 避免在构造方法中调用重写的方法

一段有坑的代码. 我们创建两个类, B 是父类, D 是子类. D 中重写 func 方法. 并且在 B 的构造方法中调用 func

class B {
    public B() {
        // do nothing
        func();
    }

    public void func() {
        System.out.println("B.func()");
    }
}

class D extends B {
    private int num = 1;

    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}

public class Test {
    public static void main(String[] args) {
        D d = new D();
    }
}

代码分析: 

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

  1. 构造 D 对象的同时, 会调用 B 的构造方法.
  2. B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  3. 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0. 如果具备多态性,num的值应该是1.
  4. 所以在构造函数内,尽量避免使用实例方法,除了final和private方法。

 输出结果:【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

结论: "用尽量简单的方式使对象进入可工作状态", 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题.

【JavaSE】面向对象编程思想之多态(图文详解),JavaSE,java,开发语言,ide,学习方法

 

到了这里,关于【JavaSE】面向对象编程思想之多态(图文详解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java不看后悔系列】|面向对象编程|[继承、封装、多态全覆盖]

    🌈个人主页:  Aileen_0v0 🔥系列专栏: Java学习系列专栏 💫个人格言:\\\"没有罗马,那就自己创造罗马~\\\" (inheritance) 1.A child class inherits everything from its parent class. 2.A child class cannot inherit the constructors from its parent class . 子类 继承 的内容: 字段(成员变量): 子类会继承父类的字段

    2024年02月05日
    浏览(37)
  • 【JavaSE专栏56】Java面向对象编程:深入理解类、对象、属性和方法的核心概念

    博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客👦🏻 《java 面试题大全》 🍩惟余辈才疏学浅,临摹之作或有不妥之处,还请读者海涵指正。☕🍭 《MYSQL从入门到精通》数据库是开发者必会基础之一~ 🪁 吾期望此文有资助于尔,即使粗浅难及深广,亦备添少许微薄

    2024年02月07日
    浏览(31)
  • 面向对象详解,面向对象的三大特征:封装、继承、多态

    一、面向对象与面向过程 面向对象编程 (Object-Oriented Programming,简称OOP)和 面向过程编程 (Procedural Programming,简称PP)是两种不同的 编程范式 。 面向对象编程强调把问题分解成对象,通过封装、继承和多态等机制,来处理对象之间的关系 。每个对象都可以独立地处理自

    2024年02月21日
    浏览(43)
  • 面向对象(类/继承/封装/多态)详解

    面向对象编程(Object-Oriented Programming,OOP)是一种广泛应用于软件开发的编程范式。它基于一系列核心概念,包括类、继承、封装和多态。在这篇详细的解释中,我们将探讨这些概念,并说明它们如何在PHP等编程语言中实现。 类是OOP的基础。它是一种用于创建对象的蓝图或模

    2024年02月08日
    浏览(51)
  • 【Java基础教程】(十四)面向对象篇 · 第八讲:多态性详解——向上及向下转型、关键字 final与 instanceof的作用~

    掌握final 的主要作用及使用; 掌握对象多态性的概念以及对象转型的操作; 掌握instanceof 的主要作用及使用; 在Java 中 final称为终结器,在Java 里面可以使用 final定义类、方法和属性,用于表示不可变性 。 final 类:当一个类被声明为 final 时,意味着该类不能被

    2024年02月16日
    浏览(33)
  • Python面向对象编程(一)类的基础,关系,继承,封装,多态

    类的一些理论概念及其应用场景等基础内容此处不赘述 目录 python中一切皆对象 类的定义及基础 属性 方法 初始化方法  普通方法 类之间的关系 相互调用 依赖关系 关联关系 组合关系 三大特征----类的继承 重写父类方法 多继承 混合继承  三大特征----封装 三大特征----多态

    2024年02月10日
    浏览(67)
  • 【C++】面向对象---多态(万字详解)

           🔥🔥 欢迎来到小林的博客!!       🛰️博客主页:✈️小林爱敲代码       🛰️文章专栏:✈️小林的C++之路       🛰️欢迎关注:👍点赞🙌收藏✍️留言       今天给大家讲解多态,多态是面向对象的一个重要内容。也非

    2024年02月01日
    浏览(32)
  • 【Java入门】-- Java基础详解之 [Java面向对象编程(初级)]

    目录 1.类与对象 2.类与对象的区别与联系 3.对象在JVM内存中的存在形式(重要) 4.属性/成员变量/字段 5.如何创建对象 6.类和对象的内存分配机制 7.面向对象的三大特征? 8.面向对象和面向过程? 9.匿名对象 10.方法(method) 11.方法的重载(overload) 12.可变形参 13.递归 14.封装 15.四种访

    2024年02月12日
    浏览(26)
  • 【深入浅出C#】章节 4: 面向对象编程基础:封装、继承和多态

    封装、继承和多态是面向对象编程中的核心概念,它们对于构建灵活、可扩展和可维护的软件系统至关重要。 封装(Encapsulation)通过将数据和相关操作封装在一个类中,隐藏内部实现细节,并提供公共接口来与外部进行交互。封装有助于保护数据的完整性和安全性,同时提

    2024年02月10日
    浏览(41)
  • Java面向对象多态

    目录 多态概述 Java 多态包括以下三种方式 方法重写(Override) 向上转型(Upcasting) 实现多态 Java 多态是指同一种类型的对象,在不同的情况下有着不同的状态和行为。它是基于继承、重写和向上转型等特性实现的,多态是面向对象编程的三大特征之一,其他两个分别是封装

    2023年04月13日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包