Java 三大特性之多态

目录

1. 多态实现的三个必要条件

1.1 继承

1.2 向上转型

1.2.1 向上转型发生的时机

1.3 重写

2. 理解多态(多态示例)

3. 使用多态的好处

3.1 类调用者对类的使用成本进一步降低

3.2 简化性

3.3 可扩展能力更强

4. 多态的实现方法

5. 向下转型


 

 多态是指同一个行为具有多个不同表现形式或形态的能力。

1. 多态实现的三个必要条件

  • 继承
  • 向上转型
  • 重写

1.1 继承

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

1.2 向上转型

向上转型就是子类对象转换为父类的引用,是天然发生的。

代码示例:


/**
 * 向上转型
 */
class Animal {
    protected String name;

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

    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }
}

class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }

    public void fly() {
        System.out.println(this.name + "正在飞 ");
    }
}

public class Demo2 {
    public static void main(String[] args) {
        // 创建一个动物类的对象
        Animal animal = new Animal("小动物");
        // 创建一个鸟类的对象
        Bird bird = new Bird("小鸟");

        // 向上转型
        Animal animal1 = new Bird("小鸟");
        System.out.println(animal.name);
        animal.eat("食物");

    }
}

 上面的代码中,我们有这样的代码:

Bird bird = new Bird("小鸟");

这个代码也可以这样写:

Bird bird = new Bird("小鸟");
Animal bird2 = bird;

或者是:

Animal animal1 = new Bird("小鸟"); 

这里的 animal1 是一个父类的引用,指向一个子类的实例。这种写法被称为向上转型。

语法:父类名称 父类引用 = new 子类();

 

1.2.1 向上转型发生的时机

  • 直接赋值
  • 方法传参
  • 方法返回

1. 直接赋值

Animal animal = new Bird();

2. 方法传参


/**
 * 向上转型
 */
class Animal {
    protected String name;

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

    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }
}

class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }

    public void fly() {
        System.out.println(this.name + "正在飞 ");
    }
}

class Cat extends Animal{
    public Cat(String name) {
        super(name);
    }
}
public class Demo2 {
    public static void main(String[] args) {
        // 创建一个动物类的对象
        Animal animal = new Animal("小动物");
        // 创建一个鸟类的对象
        Bird bird = new Bird("小鸟");
        Cat cat = new Cat("小猫");
        fun(animal,"食物");
        fun(cat,"猫粮");
        fun(bird,"虫子");

    }

    public static void fun(Animal animal, String food) {
        animal.eat(food);
    }
}

Java 三大特性之多态_第1张图片 

 Animal 有多个子类,fun 就得重载多少次。

3. 方法返回

Java 三大特性之多态_第2张图片

此时 findAnimal 返回的是一个 Animal 类型的引用,但是实际上对应的是 Bird 实例。也就是把一个子类对象转换为一个 Animal 父类的引用。

1.3 重写

子类实现父类的同名方法,并且参数的类型和个数完全相同,这种情况称为覆写/重写/覆盖。

重写的规则:

  • 重写的方法方法名、参数列表必须一致,方法返回值也要一致(除了向上转型);
  • 重写方法时子类的方法的访问权限不能低于父类的方法访问权限;
  • 普通方法可以重写,但是 static 修饰的静态方法不能重写;
  • final 修饰的方法不能被重写。

当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。要想在子类调用父类中被重写的方法,则必须使用关键字 super

重写和重载的区别可以看这篇。

2. 理解多态(多态示例)

使用多态设计程序。


/**
 * 多态
 */
class Shape {
    public void draw() {
    }
}

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

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

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

public class Demo1 {
    public static void main(String[] args) {
        Shape s1 = new Cycle();
        Shape s2 = new Rect();
        Shape s3 = new Flower();
        drawShape(s1);
        drawShape(s2);
        drawShape(s3);
    }

    private static void drawShape(Shape shape) {
        shape.draw();
    }
}

在上面这个例子中,三个对象都调用了 drawShape 方法,方法参数都是 Shape 类型,我们根据 shape 参数执行 draw 方法时发现,当传入不同对象时,draw 方法表现了不同的行为。当一个引用在不同场景下调用相同方法表现出来的不同行为。这就是多态。

3. 使用多态的好处

3.1 类调用者对类的使用成本进一步降低

多态能让类的调用者连这个类的类型时什么都不必知道,只需要知道这个对象具有某个方法即可。就好像 Animal 中有成百上千个子类,我们不需要这个类具体是啥,只要是它的子类,我们都可以使用 Aniaml 这个父类来操作子类。

3.2 简化性

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

比如让我们现在打印多个形状,如果灭有多态那代码就是:

Java 三大特性之多态_第3张图片

但是如果使用多态,就不用使用多个 if - else 分支语句,使代码更加简单。

Java 三大特性之多态_第4张图片 

3.3 可扩展能力更强

如果没有多态,新增一个子类,调用者就得先了解此类,然后在调用出再新增一个分支,很不方便。

Java 三大特性之多态_第5张图片

 

但是如果有多态,当有一个新的子类产生时,由于多态性,调用者不需要改变代码,使用父类就可以方便的扩展新的子类。

Java 三大特性之多态_第6张图片

4. 多态的实现方法

  • 重写
  • 接口
  • 抽象类和抽象方法

5. 向下转型

向上转型是子类对象转成父类对象,向下转型就是父类对象转成子类对象。

代码示例:

/**
 * 向下转型
 */

class Animal2 {
    protected String name;

    public Animal2(String name) {
        this.name = name;
    }

    public void eat(String food) {
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

class Bird2 extends Animal2 {
    public Bird2(String name) {
        super(name);
    }

    public void eat(String food) {
        System.out.println("小李是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }

    public void fly() {
        System.out.println(this.name + "正在飞");
    }
}

public class Demo3 {
    public static void main(String[] args) {
        Animal2 animal2 = new Bird2("小李");
        animal2.eat("小米");
        Bird2 bird2 = (Bird2)animal2;
        bird2.fly();
    }
}

在这个例子中,如果使用 animal2.fly() 调用 fly 的方法就会编译出错,因为 animal 的类型是 Animal, 此时编译器只知道这个类中有一个 eat 方法, 没有 fly 方法。所以如果想用 animal2 调用 fly 方法,就要使用向下转型。

Bird2 bird2 = (Bird2)animal2;
bird2.fly();

但是这样的向下转型有时会出现问题,比如:

Animal2 animal = new Cat2("小猫");
Bird2 bird = (Bird2)animal;
bird.fly();
// 执行结果, 抛出异常
Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Bird
at Test.main(Test.java:35)
animal 本质上引用的是一个 Cat 对象, 是不能转成 Bird 对象的。运行时就会抛出异常。

所以,为了让向下转型更加安全,可以先判断 animal 的本质上是否是一个 Bird 实例,再转换。可以使用 instanceof 判断,如果是,返回 true,这时再进行向下转型。

        Animal2 animal = new Cat2("小猫");
        if (animal instanceof Bird2) {
            Bird2 bird = (Bird2)animal;
            bird.fly();
        } else{
            System.out.println("不能向下转型");
        }

 

 

你可能感兴趣的:(JavaSE,java,开发语言)