多态

124 阅读7分钟

多态

多态的由来

多态在面向对象中的含义:相同的行为,不同的实现。

相同的行为的体现:

1、多态是描述行为的,也就是方法;

2、相同行为其实在本质上就是“同名”方法。

不同的实现:

同名的方法,但是个各自有各自的方法实现体,也就是“{}”及其里面的代码不同。

目前我们学过两个与之有关的概念,“重载”和“重写”

封装在面向对象中体现的是“归纳”和“信息的隐藏”;

继承在面向对象中体现的是“复用性”和“is-a关联”;

多态在面向对象中体现的是“丰富度”。

多态的分类

“静态多态”与“动态多态”

这里的“静态”和static关键字没有任何关系。

这里的“静”指的是在编译期就能够确定对象到底调用的是哪个方法;

“动”指的是程序必须在运行期才能够根据具体绑定的对象从而知道要调用的是哪个方法。

前面我们所学过的“重载”或者单独使用“重写”都属于“静态多态”。而要做“动态多态”,需要两种技术的结合:“重写”和“动态绑定”。

转型技术

所谓的“转型技术”就是类型转换。之前我们学习过一部分,比如“自动类型”转换和“强制类型”转换。

只不过我们当时学的是基本数据类型的自动类型转换和强制类型转换。

复习一下基本数据类型之间的转换

1、转换的依据

要求:小类型的可以直接自动转换为大类型的。

这里的“小”指的是?“大”指的是?

指的是该类型能够表示的数据范围谁大谁小。 而这个范围的大小是由基本数据类型的字节空间和存放形式配合起来的。

2、转换的规则

前提---boolean不参与数据类型转换

范围小的基本数据类型 自动转换成 范围大的基本数据类型。

范围大的基本数据类型 强制转换成 范围小的基本数据类型。

语法: 小类型变量 = (小类型)大类型的数据。

2-1、如果不使用强转语法,编译不通过;

2-2、如果使用强转语法,可能会有精度的丢失;

引用数据类型其实也能做强转或自动转

1、转换的依据

“小类型”的自动转换为“大类型”;

“大类型”的要强制转换为“小类型”。

这里的范围是必须在“继承关系”下进行探讨,父类(超类)代表的范围比子类(派生类)要大。只有这种情况下“大小”关系才是明确的,可以讨论的。

2、转换规则

只有有继承关系的类才能够做转换,兄弟姐妹类都不允许。

2-1、自动类型转换

在引用数据类型转换当中,这种又被称为向上转型。因为它是沿着继承树往上走。

Pet p = new Dog();

2-2、强制类型转换

又被称之为向下转型

Cat c = (Cat)new Pet();

在引用数据类型当中,强转仍然有风险。而它的风险就不仅仅是精读丢失了,而是运行时报“ClassCaseException” --- 类型转换异常!!!

引用数据类型转换的本质

1、强转也好,自动转换也好,都不是改变对象本身,而只是换一个类型的引用变量去指向这个对象。

2、编译通不通过或运行通不通过,依据是这个引用变量的类型和对象类型是否匹配。

本质上只有两种情况是没有问题的:

2-1、本类引用指向本类对象;

2-2、父类引用指向子类对象。

从面向对象场景中解释:我们说“一个动物是一只猫对象”这是完全正确的。

从内存当中解释:每一个子类对象身上有一个完整的父类对象部分,所以用父类引用指过去是可以看到一个父类对象的完整内容的,因此没有问题。

父类引用指向子类对象有何特点?

1、父类引用指向子类对象后,只能看到来自于父类当中的属性或行为(当然要受访问修饰符的限制)。

2、如果这个行为被子类重写了,那么父类引用看到的这个行为,执行的效果是子类重写后的效果。

父类引用指向子类对象后的弊端

由于变量的类型是父类类型,虽然指向了子类对象,但是在用变量做"."操作的时候,只能看到子类对象身上从父类继承而来的属性或方法,看不到子类特有的属性或方法。

解决方案:

第一步:通过强转,把父类的引用赋值给一个子类类型的变量。这样这两个引用都是指向的同一个对象,而父类类型变量能看到继承而来的属性和行为,子类类型的变量能看到所有的属性和行为。

第二步:强转是有风险的,而且引用类型的风险是运行时异常,它会导致程序停止运行直接报错。所以,在强壮前,我们必须通过判断,保证传进来的是一个可以被强转的类型。如何保证呢?

instanceof

instanceof 是一个关键字,同时它也是一个运算符!它是一个boolean运算符! 书写格式: 对象 instanceof 类型

在强转之前,一定要用instanceof做一次判断,确实该对象确实属于某个类型的时候,才能做强转。

由于现在我们知道了父类引用可以指向子类对象,所以当我们拿到一个父类引用的时候,并不知道它到底指的是那个类型的对象。有可能是父类对象,也有肯能子类A的对象,或子类B的对象,那么instanceof就可以帮我们判断它到底指的是谁!!!

多态的应用

多态参数

当一个方法需要接收参数的时候,我们把形参的类型设计为父类类型,那么该父类下的所有子类对象都可以作为参数传递进来。

当然实际使用当中,不一定是参数用父类类型的变量;属性也可以......

异构集合

在前面的学习当中,集合的内容我们学习的是数组这个概念。数组有三大特点:

1、只能存同一类型的元素;

2、空间大小一旦申明不可变;

3、在连续内存空间中存放元素。

而我们又说了数组的三大特点也是三大缺点: 其中第二点,我们在前面讲封装的过程当中,通过自定义的“Array”类可以解决掉。

class Array{
    private int[] array = new int[10];
    
    public void add(int element){
        自动往后面空白元素处放置;
        如果空间不够,就先自动扩容
    }
    
    public void remove(int index){
        自动删除该下标的元素,然后把该下标后面的元素,依次往前挪动。
        如果空余空间太大,就自动缩容
    }
    
    //另外还提供了查询、修改、获取有效元素个数等三个方法。
​
}
​

那么今天,我们学习了多态的话,就能解决第一个问题了。我们数组元素的类型声明为父类类型,那么该数组就可以装所有的子类对象了。

    父类[] 数组名 = new 父类[长度];
    数组名[下标] = 子类对象;

当然更厉害的是Object[],那么由于Object是所有类类型和数组类型的根类,因此除了基本数据类型的元素,其他的类型的对象都可以放进去了。

    Object[] objects = new Object[10];
    objects[0] = "hello";
    objects[1] = new Scanner(System.in);
    objects[2] = new MachineGun();
    objects[3] = new PureGirl("双儿");
    objects[4] = new int[4];
    objects[5] = new Object();

那如果我们把一和二的解决方法结合起来,就可以定义一个可变大可变小,也可以放置任意数据类型元素的集合了。 ---- 这就是所谓的“乾坤袋” 。