一文带你快速掌握面向对象三大特性之【多态】

发布于:2022-11-13 ⋅ 阅读:(418) ⋅ 点赞:(0)

1. 多态的概念

通俗来讲,就是多种形态。就是完成某一个行为时,如果不同对象去完成就会产生不同的状态。
总的来讲,同一件事情,发生在不同对象的身上,就会产生不同的结果。

2. 多态的实现条件

  • 1、必须在继承类下 (继承)
  • 2、子类必须要对父类中的方法进行重写 (重写)
  • 3、 通过父类的引用调用重写方法 (向上转型)

下面们,我们来详细解析下讲下这三个条件

2.1 必须在继承类下

这里我们创建 一个父类 Animal 和 两个子类 Dog 和 Cat

class Animal{
    public String name;
    public int age;
    public Animal(String name,int age){  // 带有两个参数的构造方法
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println("吃饭");
    }
}
class Dog extends Animal{
    public Dog(String name ,int age){
        super(name,age);
    }

    @Override
    public void eat() {
        System.out.println("吃狗粮");
    }
}
class Cat extends Animal{

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

    @Override
    public void eat() {
        System.out.println("吃猫粮");
    }
}

2.2 子类必须对父类中的方法进行重写

Dog类重写 eat() 方法
在这里插入图片描述

Cat类重写 eat() 方法
在这里插入图片描述


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

在测试类中,我们通过父类的引用了重写的方法

public class Test {
    public static void func(Animal animal){
        animal.eat();
    }
    public static void main(String[] args) {
        Animal animal1 = new Dog("大黄",1);
        func(animal1);

        Animal animal2 = new Cat("喵喵",2);
        animal2.eat();       // 通过父类的引用 调用重写方法
    }
}

最终的输出结果就是:
在这里插入图片描述

2.4 总结一下

我们来画个图分析下, 下面这个例子就形成了多态的条件
在这里插入图片描述


那么,程序是如何运行的呢?我们继续来看一个图,来分析下
在这里插入图片描述


在将 class文件,进行反汇编以后,也可以看出 程序在编译的时候,也是调用的 父类的 eat() 方法
在这里插入图片描述
而在运行的时候,却是调用了重写以后的方法,这就是动态绑定,也是发生多态的基础!

  • 动态绑定:也称后期绑定(晚绑定),在编译时,不能确定方法的行为,需要等到程序运行时,才能够确认具体调用哪个类的方法
  • 静态绑定:也称前期绑定(早绑定),在编译时,根据用户所传递的实参类型就确定了具体调用哪个方法,例如:函数重载

3. 什么是重写

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写。 有以下几条规则

  • private 修饰的方法不可以被修饰
  • static 修饰的方法不能被重写
  • 子类的访问权限 要 大于等于 父类的权限
  • final 关键字 修饰的方法 不能被重写,(此时这个方法叫密封方法)

既然讲到重写,那么我们就来对比下重写重载的区别

在这里插入图片描述

方法的重载是一个类的多态的表现
方法重写是子类和父类的一种多态表现

4. 向上转型和向下转型

4.1 向上转型

创建一个子类对象,将其当成父类对象来使用
语法格式: 父类类型 对象名 = new 子类类型()

Animal animal = new Cat();

使用场景有三种:

  • 直接赋值
Animal animal = new Cat("旺财",5);

  • 方法传参
public class Test {
    public static void func(Animal animal){
        animal.eat();
    }
    public static void main(String[] args) {
        Animal animal1 = new Dog("大黄",1);
        Animal animal2 = new Cat("喵喵",2);

        func(animal1);
        func(animal2);
    }
}

  • 方法的返回
    public static Animal funciton(String name){
        return new Dog("大黄",2);
    }

向上转型的优点:让代码更加灵活,可以引用更多的类型
向上转型的缺点:不能调用子类特有的方法(除非重写,发生动态绑定)

4.2 向下转型

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

        Animal animal1 = new Dog("黑咯破",1);
        Dog dog = (Dog) animal1;

向下转型用的比较少,不安全,可以从下图中看出不安全。

在这里插入图片描述


Java 为了提高向下转型的安全性,引入 instance关键字,如果该表达式 为 true,则可以安全转换。
在这里插入图片描述

5.1 使用多态的好处和缺陷

好处:

  • 能够降低代码的 圈复杂度,避免使用大量的 if-else
  • 可扩展能力更强

多态也有缺陷:

  • 属性没有多态
  • 构造方法没有多态

我们要避免在构造方法中调用重写的方法

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();
	}
}

上述代码执行的结果是 D.fun() 0 , 通过下图来看执行步骤

在这里插入图片描述

当在父类的构造方法中 去调用父类和子类的重写方法时,此时会调用子类的!

本文含有隐藏内容,请 开通VIP 后查看