Java、多态

📚 目录

前言:

面向对象编程有三大核心特性:封装、继承、多态。其中多态是 思想的精髓,它让程序具备更强的灵活性和可扩展性 ------ 允许不同子类对象以统一的父类形式被调用,却能执行各自独特的逻辑。

1. 什么是多态?

字面意思:一个事物多种形态。

具体点:就是不同的对象去完成时展现出不同的形态。

通俗来讲:我们通过狗和猫的对象去访问同一个东西的时候,狗去访问的时候会汪汪叫,而用猫去访问的时候就会变成喵喵叫。
[🔙 返回目录](#🔙 返回目录)


2. 实现多态的前提

想要实现多态的前言条件:

什么是重写?

什么是向上转型和向下转型?

什么是动态绑定?

在知道这三者的基础上,我们就能够实现多态。
[🔙 返回目录](#🔙 返回目录)


2.1重写

重写重新,顾名思义就是重新对一个方法进行写。(只有方法能够重写)!!!

想必大家在学校学习的时候都经历过重写。

重写可以理解为:在抄一篇古诗,古诗名、内容、作者不变,但是字迹是变化的。

Java中可以理解为:返回值和形参不能改变,也就是外壳不发生改变。核心在与重写方法的内部的结构。

多态是在继承的基础上开展的。
注意:
  被(private、final、static)修饰的父类方法以及父类的构造方法不能被子类重写。
  多态中重写的方法的返回值可以不同,但是必须是父子关系,才能进行重写。

父类:

java 复制代码
public class Animal {
    String name;
    public Animal(String name) {
        this.name = name;
    }
    public Animal eat() {
        System.out.println(name+"正在吃饭...");
        return null;
    }
}

子类:

java 复制代码
public class Dog extends Animal{

    public int age;
    public Dog(String name) {
        super(name);
    }

    @Override//代表这个方法被重写
    public Dog eat() {//返回值不同 
        System.out.println(name+"正在吃狗粮...");
        return null;
    }
}

**  @Override代表当前方法被重写。
  重写的方法的修饰访问限定符要大于等于被重写的方法。(private < 默认的 < protected < public)但是被private修饰的不能被重写。**

父类:

java 复制代码
public class Animal {
     void eat() {
        //...
    }
}    

子类:

java 复制代码
public class Cat extends Animal{
    @Override
    public void eat() {
        //...
    }
}    
java 复制代码
public class Cat extends Animal{
    @Override
    protected void eat() {
        //...
    }
}    

我们可以认为重写是多态的一种多形态的表现。
[🔙 返回目录](#🔙 返回目录)


2.2向上转型和向下转型

知道完重写,就可以开始多态的第二步。------>向上转型和向下转型。

向上转型:让父类引用 子类 的对象

形式:

父类:

java 复制代码
public class Animal {
    String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat() {
        System.out.println(name+"正在吃饭...");
    }
}

子类:

java 复制代码
public class Cat extends Animal{
    public Cat(String name) {
        super(name);
    }
    @Override
    public void eat() {
        System.out.println(name+"正在吃猫粮...");
    }
}    

测试方法:当然我们也能把两个对象先创建出来再进行让父类引用子类。

java 复制代码
public class Test {
    public static void main(String[] args) {
        Animal animal = new Cat("咪咪");
    }
}

当然我们能从结果上面看到:结果是子类的结果,而不是父类。

注意:
  我们在向上转型的过程中只能调用父类自己原本有的方法,而不是子类有父类没有的方法;否则编译器会发生报错。
  例如我们在子类中增加了一个方法。

当父类的方法的返回类型为自己的类型的时候,子类的返回值也可以是子类本身。

父类:

java 复制代码
public class Animal {
    public Animal eat() {//返回值为自己类本身
        System.out.println(name+"正在吃饭...");
        return null;
    }
}

子类:

java 复制代码
public class Cat extends Animal{
    @Override
    public Cat eat() {//返回值为子类的类型
        System.out.println(name+"正在吃猫粮...");
        return null;
    }
}    

注意:

只有两类为父子关系的时候才能这么写,否则编译器会报错。

向下转型:子类的对象 引用 父类的对象

注意:引用的时候需要强制类型转换:父类范围比子类大
**  将⼀个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法。**

例如:

java 复制代码
class Animal {
    public void sleep() { 
        // 父类方法
        System.out.println("动物正在睡觉觉...");
    }
}

class Cat extends Animal {
    public void eat() { 
        // 子类独有方法
        System.out.println("猫正在猫粮...");
    }
}

public class Test {
    public static void main(String[] args) {
        // 情况1:直接创建Cat对象(子类引用)
        Cat cat1 = new Cat();
        cat1.sleep(); // 能调父类方法
        cat1.eat(); // 能调子类独有方法

        // 情况2:向上转型(父类引用)
        Animal animal = new Cat();
        animal.sleep(); // 能调父类方法
        // animal.catchMouse(); 编译报错!编译器认为Animal没有这个方法
        
        // 情况3:向下转型后(还原子类引用)
        Cat cat2 = (Cat) animal;
        cat2.eat(); // 能调子类独有方法
    }
}

注意:
  向下转型不安全(比如猫可以是动物,但是动物不一定是猫)!!!
  向下转型只是讲动物还原成猫(而它的本质不是猫)!
  我们一般情况下对向下转型的使用频率较低。
  向下转型不安全,而Java官方给我们提供了关键字:instanceof ;来帮助我们检测向下转型是否成功。

例如:

java 复制代码
public class Test {

    public static void main(String[] args) {
        Animal animal = new Cat("咪咪");
        Cat cat = (Cat)animal;//向下转型
        if(animal instanceof Cat) {
            cat.sleep();
        } else {
            return;
        }
    }
}

**  如果animal不是Cat的父类编译器就会报错。**
[🔙 返回目录](#🔙 返回目录)


2.3动态绑定

字面意思:在运行的时候会进行绑定。

Java中是什么意思呢?

父类:

java 复制代码
public class Animal {
    String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat() {
        System.out.println(name+"正在吃饭...");
    }
}

子类:

java 复制代码
public class Dog extends Animal{
    public int age;
    public Dog(String name) {
        super(name);
    }
    @Override
    public void eat() {
        System.out.println(name+"正在吃狗粮...");
    }
}

创建一个测试类:

java 复制代码
public class Test {

    public static void main(String[] args) {
        Animal animal = new Dog("旺财");
        animal.eat();
    }
}    

我们掉到cmd界面来看:

**  在编译的时候编译器认为调用的是Animal这个类中的eat方法,而实际情况下调用的是Dog这个类中的方法。
  在代码运行过程中对Dog类中eat方法进行绑定,这就叫动态绑定。**
[🔙 返回目录](#🔙 返回目录)


3. 多态的实现

知道了重写、向上向下转型、动态绑定:我们就能知道多态这个概念了。

实现多态:

父类:

java 复制代码
public class Animal {
    String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat() {
        System.out.println(name+"正在吃饭...");
    }
}

子类:

java 复制代码
public class Dog extends Animal{
    public int age;
    public Dog(String name) {
        super(name);
    }
    @Override
    public void eat() {
        System.out.println(name+"正在吃狗粮...");
    }
}

多态的实现:

java 复制代码
public class Test {

    public static void main(String[] args) {
        Animal animal = new Dog("旺财");
        animal.eat();
        Animal animal1 = new Cat("咪咪");
        animal1.eat();
    }
}    

通过不同的对象去访问同一个方法,得到的结果是不同的。

[🔙 返回目录](#🔙 返回目录)


4. 多态的好处

优点:

降低代码的圈复杂度。

拓展能力变强了。

圈复杂度:举个例子(我们创建了很多个家庭动作类型)

java 复制代码
public class Test1 {

    public static void main(String[] args) {
        Father father = new Father();
        Me me = new Me();
        Sister sister = new Sister();
        Mother mother = new Mother();
        String[] doing = {"father","me","mother","sister"};
        if(doing.equals("father")) {
            father.toDo();
        }else if(doing.equals("me")) {
            me.toDo();
        }else if(doing.equals("mother")) {
            mother.toDo();
        }else if(doing.equals("sister")) {
            sister.toDo();
        }
    }
}    

有很多if语句就会一圈套一圈,非常复杂。

而我们多态就能这么来做。

一共加起来才这几行代码。大大美化了我们的书写风格,显得非常高大上。
注意:
  在我们多态中避免使用重名的方法:

父类:

java 复制代码
public class Do {
    public Do() {
        func();
    }
    public void func() {
        System.out.println("Do");
    }
}

子类:

java 复制代码
public class Me extends Do{
    public int a = 10;

    @Override
    public void func() {
        System.out.println("Me:"+a);
    }
}

调用:

java 复制代码
public class Test1 {


    public static void main(String[] args) {
        Do doing = new Me();
    }
}    

我们发现a的值为0。
**  原因:优先完成父类的构造方法,父类构造方法中调用了与子类同名的方法,他们构成了多态,进入到子类func方法中,父类与子类构造方法还没走完,子类的实例成员变量a还没有进行赋值,此时a的值就为默认值0。**
[🔙 返回目录](#🔙 返回目录)


相关推荐
feng尘2 小时前
Java线程池的执行流程与常见配置
java
天天学IT2 小时前
第二章 Qt 模块
开发语言·qt·qt教程·qt6教程
yaoxin5211232 小时前
364. Java IO API - 复制文件和目录
java·开发语言
独断万古他化2 小时前
【Java 实战项目】多用户网页版聊天室:项目总览与用户 & 好友管理模块实现
java·spring boot·后端·websocket·mybatis
念念不忘 必有回响2 小时前
Drizzle ORM上手指南:在Next.js中优雅地操作PostgreSQL
开发语言·postgresql·nodejs·nextjs·drizzle
Sylvia-girl2 小时前
c语言-2-数据类型和变量
c语言·开发语言
白露与泡影2 小时前
金三银四高频 Java 面试题及答案整理 (建议收藏)
java·开发语言
小杍随笔2 小时前
【Rust 半小时速成(2024 Edition 更新版)】
开发语言·后端·rust
tsyjjOvO2 小时前
SpringBoot 整合 MyBatis
java·spring boot·mybatis