java基础---24. 多态性

1 概述

  • 面向对象三大特征:封装性、继承性、多态性。其中,继承性是多态性的前提。

  • 继承性在java中的体现除了extends还有implements。所以extends继承和implements实现都是继承性的前提。

  • 不一定是类与类之间的继承,还有可能是接口与接口之间的继承,还有可能是类与接口之间的实现

  • 注意:多态性指的是对象而不是类

2 多态的格式与使用

代码当中体现多态性,其实就是一句话:父类引用指向子类对象。

  • 格式
//方式1:
父类名称 对象名 = new 子类名称();//子类对象就被当作父类进行使用,一只猫被当中动物来看待
//或者
//方式2
接口名称 对象名 = new 实现类名称();
对于方式1举例
  • 定义一个父类
public class Fu {
    public void method(){
        System.out.println("父类方法");
    }
    public void methodFu() {
        System.out.println("父类特有方法");
    }
}
  • 定义一个子类
public class Zi extends Fu{
    @Override
    public void method() {
        System.out.println("子类方法");
    }
}
  • 调用
public class Demo01Multi {
    public static void main(String[] args) {
        //使用多态写法
        //左侧父类的引用指向右侧子类的对象
        Fu obj = new Zi();
        obj.method();//子类方法       对于成员方法,new的是谁就调用谁的方法 若没有就往上找
        obj.methodFu();//父类特有方法
    }
}

3 多态中成员变量的使用(规则不变)

规则:访问成员变量两种方式

  • 1.直接通过对象名称访问成员变量(对象名称.变量名):看等号左边是谁,优先用谁,没有则向上找。
  • 2.间接通过成员方法访问成员变量:该方法属于谁则优先用谁,没有则向上找。
    注意:成员变量不可以覆盖重写,只有方法可以覆盖重写
举例
  • 定义一个父类
public class Fu {
    int num = 10;
    public void showNum(){
        System.out.println(num);
    }
}
  • 定义一个子类
public class Zi extends Fu {
    int num = 20;
    int age = 18;
}
  • 使用
public class Demo01MutilField {
    public static void main(String[] args) {
        //使用多态写法,父类引用指向子类对象
        Fu obj = new Zi();//等号左边是Fu

        //1.
        System.out.println(obj.num);//10
        //System.out.println(obj.age);//错误写法,Fu中没有age,再向上找是Oject类也不会有age

        //2.
        obj.showNum();//10   子类没有对该方法覆盖重写则就向上找父输出10;若子类对该方法覆盖重写那就是子输出20

    }
}

4 多态中成员方法的使用(规则不变)

规则:看new的是谁,就优先用谁,没有则向上找
口诀:编译看左边,运行看右边
对于成员变量的口诀:编译看左边,运行还看左边

举例
  • 定义一个父类
public class Fu {
    public void method(){
        System.out.println("父类方法");
    }
    public void methodFu(){
        System.out.println("父类特有方法");
    }
}
  • 定义一个子类
public class Zi extends Fu {
    @Override
    public void method(){
        System.out.println("子类方法");
    }

    public void methodZi(){
        System.out.println("子类特有方法");
    }
}
  • 使用
public class Demo02MutilMethod {
    public static void main(String[] args) {
        Fu obj = new Zi();//多态
        obj.method();//子类方法
        obj.methodFu();//父类特有方法    子类没有改方法,所以向上找到了父类的该方法

        //obj.methodZi();//错误写法
        //编译看左边,左边是Fu,而Fu当中没有methodZi方法,所以出现红线,编译报错
    }
}

5 多态的好处

调用方法的时候,编译看左边,运行看右边

6 对象的向上转型

举例

本例用的是类当然也可以用接口

  • 定义一个Animal类
public abstract class Animal {
    public abstract void eat();
}
  • 定义一个Cat类
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
  • 使用
public class Demo01Main {
    public static void main(String[] args) {
        //对象的向上转型就是:父类引用指向子类对象
        Animal animal = new Cat();
        animal.eat();//猫吃鱼
        //编译看左:左边Animal有eat方法
        //运行看右:运行右边Cat的eat方法
    }
}
举例(接口)
  • 定义一个接口
public interface Animal {
    public abstract void eat();
}
  • 定义一个实现类
public class Cat implements Animal{
    @Override
    public void eat() {
        System.out.println("猫爱吃鱼鱼");
    }
}
  • 使用
public class Demo01Main {
    public static void main(String[] args) {
        Animal animal = new Cat();
        animal.eat();//猫爱吃鱼鱼
    }
}

7 对象的向下转型

问题引入

向上转型是安全的,没有问题。但是有弊端:对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。

  • 父类
public abstract class Animal {
    public abstract void eat();
}
  • 子类
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    //子类特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}
  • 使用
public class Demo01Main {
    public static void main(String[] args) {
        //对象的向上转型就是:父类引用指向子类对象
        Animal animal = new Cat();
        animal.eat();//猫吃鱼
        //编译看左:左边Animal有eat方法
        //运行看右:运行右边Cat的eat方法

        //animal.catchMouse();//错误写法  编译报错  Animal中没有这个方法  并不是所有的动物都会猫抓老鼠
    }
}
解决方案:向下转型【还原】

向上转型一定安全,向下转型有前提条件

  • 父类
public abstract class Animal {
    public abstract void eat();
}
  • 子类Cat
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    //子类特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}
  • 子类Dog
public class Dog extends Animal{
    @Override
    public void eat(){
        System.out.println("狗吃骨头");
    }

    public void watchHouse(){
        System.out.println("狗狗看家");
    }
}
  • 使用
public class Demo01Main {
    public static void main(String[] args) {
        //对象的向上转型就是:父类引用指向子类对象
        Animal animal = new Cat();//本来创建的时候是一只猫
        animal.eat();//猫吃鱼


        //animal.catchMouse();//错误写法  编译报错  Animal中没有这个方法  并不是所有的动物都会猫抓老鼠

        //向下转型进行还原动作
        Cat cat = (Cat)animal;
        cat.catchMouse();//猫抓老鼠

        //错误的向下转型
        //本来new的时候是一只猫,现在非要当作狗狗
        Dog dog = (Dog)animal;//编译不报错,但是运行异常:java.lang.ClassCastException类转换异常
    }
}
新的问题:如果知道父类的引入本来是Cat还是Dog呢?
  • 格式
对象 instanceof 类名称

这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例。

public class Demo02Instanceof {
    public static void main(String[] args) {
        Animal animal = new Cat();//本来就是一只猫
        animal.eat();//猫吃鱼
        giveMePet(new Cat());
    }
    
    public static void giveMePet(Animal animal){
        //如果希望用子类特有方法,需要向下转型
        //判断传入的参数本来是猫还是狗,再决实施行何种向下转型的方案
        if(animal instanceof Dog) {//判断一下父类引用animal本来是不是狗
            Dog dog = (Dog)animal;
            dog.watchHouse();
        }
        if(animal instanceof Cat){//判断一下父类引用animal本来是不是猫
            Cat cat = (Cat)animal;//猫抓老鼠
            cat.catchMouse();
        }
    }
}
原文地址:https://www.cnblogs.com/deer-cen/p/12247799.html