五、多态 讲解

🚀 一、多态的概念

通俗地讲:多态就是完成某个行为时,当对象不同时会发生不同的状态。

🌟 1.2 多态实现条件

在 Java 中要实现多态,必须要满足以下条件:

  1. 必须在继承的体系下。
  2. 子类必须要对父类中的方法进行重写。
  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(this.name + "在吃饭");
    }
}

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

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

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

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


// 分割线 


public class Base{
    // 编译器在编译代码时,并不知道调用的是哪个类中的方法
    // 只有程序运行起来后,形参 a 引用的具体对象才确认,才知道调用哪个方法。
    // 注意:此处形参类型必须是父类的类型才可以。
    public static void eat(Animal a){
        // 发生了动态绑定。
        a.eat();
    }

    public static void main(String[] args) {
        Cat c1 = new Cat("猫",2);
        Dog d1 = new Dog("狗",1);
        
        eat(c1);
        eat(d1);
    }
}

上面的代码中,分割线上方的代码是 类的实现者 编写的,分割线下方的代码是 类的调用者 编写的。

🌓
当类的调用者在编写 eat 这个方法时,参数类型是 Animal ,此时在方法内部不知道当前的 a 引用指向哪个类型的实例。此时 a 这个引用调用 eat 方法可能有多种不同的表现(和 a 引用的实例相关),这样的行为就叫多态。

🚀 二、重写

重写(override):也称为覆盖。重写是子类对父类非静态、非 private 修饰,非 final 修饰、非构造方法的实现过程进行重新编写,返回值和形参都不变。即外壳不变,核心重写。

重写的好处:子类可以根据需要,定义自己特定的行为。也就是子类可以根据需要实现父类的方法。

🌈方法重写的规则:

  1. 子类在重写父类方法时,一般必须与父类原型方法一致:修饰符、返回值类型、方法名(参数列表)要完全一致。
  2. 子类重写方法的访问权限不能比父类中原方法的更低。
  3. 父类被static、private修饰的方法和构造方法都不能被重写。
  4. 重写的方法,应该使用 @override 来注释。

🌈重写和重载的区别:
在这里插入图片描述
方法重载是一个类的多态性表现。而方法重写是子类和父类之间的多态性表现。

// 重载
class Dog{
	// 方法名相同,参数列表不同
	public void eat(){
		System.out.println("");
	}
	@overload
	public void eat(String name){
		System.out.println("");
	}
}


// 重写
class Dog{
	public void bark(){
		System.out.println("woof!");
	}
}

class Hound extends Dog{
	public void sniff(){
		System.out.println("sniff");
	}
	
	// 重写了父类的 bark 方法,外壳相同,核心不同。
	@override
	public void bark(){
		System.out.println("bowl!");
	}
}

🌟 2.1 静态绑定和动态绑定

静态绑定:
在编译时,根据用户所传递的实参类型和个数就确定了具体调用的是哪个方法。典型代表是函数重载。
动态绑定:
编译时,并不能确定方法的具体行为,需要在程序运行时,才能够确定具体调用的是哪一个类的方法。
前提: 1. 父类引用子类的对象,即发生向上转型。2. 通过父类调用和子类同名的重写方法。

🚀 三、向上转型和向下转型

🌟 3.1 向上转型

创建一个子类对象,并当成父类对象使用。

父类类型 对象名  =  new  子类类型();

Animal animal = new Dog();

使用场景:

  1. 直接赋值。
  2. 方法传参。
  3. 返回类型。
// 2. 方法传参,形参为父类类型的引用,可以接收任意的子类对象。
public static void eat(Animal a){
	a.eat();
}

// 3. 做返回值类型,返回任意子类对象。
public static Animal buyAnimal(String a){
	if(a == "dog"){
		return new Dog();
	}
	else if(a == "cat"){
		return new Cat();
	}
	else{
		return null;
	}
}

🔺 特点:

  • 向上转型让代码更加灵活。
  • 向上转型不能调用到子类特有的方法。

🌟 3.2 向下转型

子类在经过向上转型之后作为父类对象使用,无法再调用子类的方法,但是要想调用子类特有的方法,只需向下转型即可。
在这里插入图片描述

// 向上转型一个 Animal 类
Animal animal = new Cat();
        
// 强制向下转型
cat = (Cat)animal;
cat.eat();

向下转型并不安全,如果转换失败,运行时会出现异常。Java 中为提高向下转型的安全性,引入了 instanceof ,如果表达式为 true 则安全转换。

public static void main(String[] args) {
        Animal animal = new Cat();

		// 哪个安全,animal 就会向下转型为哪个类型。
        if(animal instanceof Cat){
            Cat cat = (Cat)animal;
            cat.eat();
        }
        if(animal instanceof Dog){
            Dog dog = (Dog)animal;
            dog.eat();
        }
}

🚀 四、多态的优缺点

优点:

  1. 降低代码的 “圈复杂度” ,避免使用大量的 if - else 。
  2. 可拓展能力强。

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

避免在构造方法中调用重写的方法。
用尽量简单的方式使对象进入可工作状态。尽量不在构造器中调用方法,如果这个方法被子类重写,就会触发动态绑定,但是此时子类的构造还未完成。可能会出现隐藏的极难发现的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值