目录

  • 1 多态概述
  • 2 多态的格式
  • 3 多态的简单应用
  • 4 多态中的类型转换
  • 4.1 向上转型
  • 4.2 向下转型
  • 4.2.1 为什么向下转型
  • 4.2.2 向下转型的异常
  • 4.2.3 向下转型的异常处理办法
  • 5 注意事项


1 多态概述

概念:

  1. 多态:同一行为,具有多种不同表现形式。
  2. 多态是面向对象的三大特征之一(封装、继承、多态)。
  3. 生活中,比如跑的动作,猫和狗跑起来不同;飞的动作,昆虫和鸟类飞起来不同。可见,同一行为,通过不同的事物,体现出不同的形态。多态描述的就是这种概念。

多态的前提

  1. 继承或实现【二选一】
  2. 方法被重写【不重写,无意义】
  3. 父类引用子类对象【格式体现】

2 多态的格式

格式

父类类型 变量名 = new 子类对象;
变量名.方法名();

Fu f = new Zi();
f.method();

父类类型:子类继承的父类类型,或子类实现的父接口类型

使用过程

  1. 使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,编译错误;
  2. 如果有,执行的是子类重写后的方法。

代码示例

// 定义父类
public abstract class Animal {
    public abstract void eat();
}

// 定义子类
class Cat extends Animal {
    public void eat() {
        System.out.println("吃鱼");
    }
}

class Dog extends Animal {
    public void eat() {
        System.out.println("啃骨头");
    }
}

// 定义测试类
public class Test {
    public static void main(String[] args) {
        // 多态形式,创建对象
        Animal a1 = new Cat();
        // 调用的是Cat的eat
        a1.eat();

        // 多态形式,创建对象
        Animal a2 = new Dog();
        // 调用的是Dog的eat
        a2.eat();
    }
}

吃鱼
啃骨头

3 多态的简单应用

实际开发中,父类类型作为方法形式参数,传递子类对象给方法,进行方法调用,更能体现出多态的扩展性与便利。

// 定义父类
public abstract class Animal {
    public abstract void eat();
}

// 定义子类
class Cat extends Animal {
    public void eat() {
        System.out.println("吃鱼");
    }
}

class Dog extends Animal {
    public void eat() {
        System.out.println("啃骨头");
    }
}

// 定义测试类
public class Test {
    public static void main(String[] args) {
        // 多态形式,创建对象
        Cat c = new Cat();
        Dog d = new Dog();

        showAnimalEat(c);
        showAnimalEat(d);
    }

    public static void showAnimalEat(Animal a){
        a.eat();
    }
}

吃鱼
啃骨头

4 多态中的类型转换

多态的转型分为向上转型和向下转型两种。

4.1 向上转型

向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。

当父类引用指向一个子类对象时,便是向上转型。

使用格式:

父类类型 变量名 = new 子类类型();

4.2 向下转型

向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。

一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

格式:

子类类型 变量名 = (子类类型) 父类变量名;

4.2.1 为什么向下转型

当使用多态方法调用方法时,首先检查父类中是否有该方法,如果没有则编译错误。也就是说,不能调用子类拥有而父类没有的方法。所以,想要调用子类特有的方法,必须向下转型。

代码示例

// 定义父类
public abstract class Animal {
    public abstract void eat();
}

// 定义子类
class Cat extends Animal {
    public void eat() {
        System.out.println("吃鱼");
    }
    public void catchMouse() {
        System.out.println("抓老鼠");
    }
}

class Dog extends Animal {
    public void eat() {
        System.out.println("啃骨头");
    }
    public void watchHouse() {
        System.out.println("看家");
    }
}

// 定义测试类
public class Test {
    public static void main(String[] args) {
        // 向上转型
        Animal a = new Cat();
        a.eat();

        // 向下转型
        Cat c = (Cat)a;
        c.catchMouse();
    }
}

吃鱼
抓老鼠

4.2.2 向下转型的异常

向下转型的过程有可能会出现以下异常情况

public class Test {
   public static void main(String[] args) {
        // 向上转型
        Animal a = new Cat();
        // 调用的是Cat的eat
        a.eat();

        // 向下转型
        Dog d = (Dog)a;
        d.watchHouse();//出错
    }
}

程序会报出ClassCastException异常,因为Cat类型向上转型,再向下转型必然也是Cat类型,不能是Dog类型,这两个类型没有任何继承关系,不符合类型转换的定义。

4.2.3 向下转型的异常处理办法

为避免向下转型过程中类型转换异常的出现,使用instanceof关键字给引用变量做类型检验。

格式:

变量名 instanceof 数据类型

1 如果变量属于该数据类型,返回true
2 如果变量不属于该数据类型,返回false

所以,在向下转换前,我们最好先做一个判断,代码如下:

public class Test {
    public static void main(String[] args) {
        // 向上转型
        Animal a = new Cat();
        a.eat();

        // 向下转型
        if (a instanceof Cat) {
            Cat c = (Cat)a;
            c.catchMouse();
        } else if (a instanceof Dog) {
            Dog d = (Dog)a;
            d.watchHouse();
        }
    }
}

5 注意事项

  1. 成员方法:new的谁就调谁,这就是多态。(编译看左边,运行看右边)
  2. 成员变量:this在哪个类就指向哪个类的成员变量,没有多态。(编译看左边,运行还看左边)