多态性

发布时间 2023-08-02 15:07:20作者: 你闯进我的星球

多态性

引入:传统的方法(形参为不同类,就要新建不同的方法)代码复用性不高,不利于代码维护

多(多种)态(状态):方法或对象具有多种形态

多态的具体体现:

  1. 方法的多态

    方法的重载和重写都体现了多态

  2. 对象的多态

对象的多态

一个对象的编译类型和运行类型可以不一致,编译类型在定义对象时就确定了,不能改变,而运行类型可以变化,编译类型看定义时“=”的左边,运行类型看”=“的右边

编译类型和运行类型
Animal animal = new Dog();  	//编译类型为Animal,运行类型为Dog
animal = new Cat();				//animal的编译类型仍为Animal,但运行类型为Cat父类的引用指向子类的对象(或子类的对象赋给父类的引用)
person p = new student();
向上转型

本质:父类的引用指向子类的对象

语法:

父类类型 引用名 = new 子类类型();

特点:

  1. 可以调用父类中的所有成员(需遵守访问权限)

  2. 不能调用子类的特有成员

    Animal animal = new Cat();
    //只能调用Animal中的方法,不能调用Cat类中的特有方法
    

    解释:在编译阶段,对象能调用哪些成员由编译类型决定,比如上例中在编译阶段编译器认为animal的类型为Animal,因此只能调用Animal中的成员,如果调用Cat类中的特有方法将不能被编译器识别

    image-20211207201938817

  3. 一旦编译成功,进入运行阶段,那么将会依据对象的运行类型,在调用方法时从运行类型处开始查找方法,如果运行类型中没有声明此方法,则在向上(父类中)寻找,直至找到Object类。因此一旦子类中重写了父类的方法,那么由于先从子类中查找声明方法,因此会执行该重写的方法。

向下转型

语法:

子类类型 引用名 = (子类类型)父类引用

特点:

  1. 只能强转父类的引用,不能强转父类的对象

  2. 要求父类的引用必须指向的是目标子类类型的对象(父类引用的运行类型为什么类型,向下转型才能转为哪个类型)

    Animal animal = new cat();//父类引用animal
    //向下转型
    Cat cat = (Cat)animal;//目标子类类型Cat
    
    Dog dog =(Dog)animal;//报错
    
  3. 向下转型后可以调用子类类型中所有的成员

属性问题

属性的值由编译类型决定

class Animal{
    int ID = 1;
}
class Cat{
    int ID = 2;
}
Animal animal = new Cat();
int ID=animal.ID	//ID=1
image-20211208151106267

动态绑定机制

  1. 当调用对象方法时, 方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明使用哪里的属性值
public class A{
    int i = 10 ;
    public int sum(){
        return 10+getsum();
    }
    public int getsum(){
        return i
    }
}
public class B extends A{
    int i = 20;
    public int getsum(){
        return i
    }
}
public class test{
    public static void main(String[] args){
        A a = new B();
        int isum=a.sum();
        //isum=30。首先在运行类型B中看是否有sum方法,没有向父类中寻找
        //父类中的sum方法中又涉及到getsum()方法,而子类和父类都有这个方法
        //由动态绑定机制知,方法会和对象的运行类型绑定,因此会从运行类型B处寻找该方法
        //getsum返回结果20
    }
}

多态应用

多态数组

数组的声明类型为父类类型,但里面保存的元素类型为父类/子类类型

image-20211208155839492

需要注意的时,子类对象不能调用自己特有的方法,如果需要调用特有方法,需要进行向下类型转换

多态参数

方法定义的形参为父类类型,实参类型可以为子类类型

image-20211208160930014

对于对象p,如果想调用其方法时,只能调用父类中所声明的方法,但是如果子类中对父类中方法进行重写,那么则会调用子类中重写的方法,而对于子类中单独声明的方法则不能被调用。因此应用多态性需满足两个前提条件:①类的继承②子类中有重写的方法。

对象的多态性只适合于方法,不适用于属性

//举例1
class Test{
    public static void main(String[] args){
        Test t = new Test();
        t.test(new Dog());
        t.test(new Cat());
    }
    //多态性体现:父类对应多个子类,作为函数形参多个子类都可传入到函数中,而不需要创建多个不同函数形参的函数
    public void test(Animal animal){ 
        animal.eat();
    }
}
public class Animal{
    public void eat(){
        System.out.println("吃饭!");
    }
}
public class Dog extends Animal{
    public void eat(){
        System.out.println("吃骨头!");
    }
}
public class Cat extends Animal{
    public void eat(){
        System.out.println("吃鱼!");
    }
}
//举例2
class order{
    public void method(Object obj){
        
    }
}

多态性是一种运行时行为

image-20211207162259900

向下转型

如果使用多态new一个对象,那么这个对象并不能调用子类中的特有方法,但是如果想调用子类中的特有方法,可通过”向下转型(使用强制类型转换符)“,当然使用强转时可能会出现异常(ClassCastException异常)。

people p = new student();//此时p不能调用student的特有方法
student s = (student)p;//通过向下转型,s可以调用student的特有方法

如果想要向下转型成功,则new的必须为其子类

image-20211207171141512
//情况1
person p = new person();
man m = (man)p;		//报错
//情况2
object p = new man();
person m =(person)p;	//正常运行,man为person的子类,强转为man可以,则强转为person也可以
//情况3
person p = new woman();
man m = (man)p;    //报错
//情况4 直接"="
man p = new woman();	//报错
//只有两者类型相等或者是其子类的情况下才能直接用"="

instanceof关键字

a instanceof A //判断对象a的运行类型是否是类A或其子类,如果是返回true,如果不是返回false

使用情境:为了避免在向下转型时出现“ClassCastException“异常,在进行向下转型之前先用instanceof进行判断,如果为true才向下转型。

people p = new man();
if(p instanceof man){ //p instanceof man结果为true
    man m = (man)p;	  //进行转型
}