💞 Java多态
1. 什么是多态?
多态是方法或对象具有多种形态。
即同一方法可以根据发送对象的不同而采用多种不同的行为方式。一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)。
所谓多态就是指一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,在编译时并不确定,必须在程序运行期才确定。
Java中的多态,分为编译时多态和运行时多态。
- 编译时多态:主要是通过 方法的重载(overload)来实现,Java会根据方法参数列表的不同来区分不同的方法,在编译时就能确定该执行重载方法中的哪一个。这是静态的多态,也称为静态多态性、静态绑定、前绑定。但也有一种特殊的方法重写的情况,属于编译时多态。在方法重写时,当对象的引用指向的是当前对象自己所属类的对象时,也是编译时多态,因为在编译阶段就能确定执行的方法到底属于哪个对象。
- 运行时多态:主要是通过方法的重写(override)来实现,让子类继承父类并重写父类中已有的或抽象的方法。这是动态的多态,也称为”后绑定“,这是我们通常所说的多态性。
一句话,如果我们在编译时就能确定要执行的方法属于哪个对象、执行的是哪个方法,这就是编译时多态,否则就是运行时多态!
2. 多态的前提
- 需要存在继承或者实现关系: 多态发生在继承关系中,必须存在有继承关系的父类和子类中,多态建立在封装和继承的基础之上;
- 有方法的重写: 必须要有方法的重写,子类对父类的某些方法重新定义;
- 父类引用指向子类对象(向上转型): 就是要将父类引用指向子类对象,只有这样该引用才既能调用父类的方法,又能调用子类的方法。
只有满足了以上3个条件才能实现多态,开发人员也才能在同一个继承结构中,使用统一的代码实现来处理不同的对象,从而执行不同的行为。
3. 代码实现
3.1 定义父类
1 | package com.alvis.object.blog; |
3.2 定义子类
1 | package com.alvis.object.blog; |
3.3 定义测试类
1 | package com.alvis.object.blog; |
我们可以看到上述代码,满足了多态的3个必要条件:继承、重写、向上转型!有子类继承父类,有方法重写,存在向上转型。而且根据这个案例,我们可以进一步理解多态的含义和特点。在多态中,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法!
本次的最终结果输出如下:
3.4 结果分析总结
Java实例方法的调用,是基于运行时实际类型的动态调用,而非声明的变量类型!通俗地说,就是我们调用的到底是哪个对象的方法,不是由=号左侧声明的引用变量来决定的,而是由=号右侧的实际对象类型来决定的!
这也是多态的一个重要特征!所以我们说在多态中,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法!即只有在运行期,才能动态决定调用哪个子类的方法。 这种不确定性的方法调用,究竟有什么作用呢?其实主要就是允许我们能够添加更多类型的子类,实现对父类功能的扩展,而不需要修改父类的代码。
规则总结:
当父类引用指向子类对象时,父类只能调用执行那些在父类中声明、被子类覆盖的子类方法,而不能执行子类独有的成员方法。 如 1,2
当子类和父类有相同属性时,父类会调用自己的属性。 如 3
补充: 在进行向上转型时,父类引用调用同名的静态方法时,执行的是父类中的方法。这是因为在运行时,虚拟机已经确定了static方法属于哪个类。“方法重写”只适用于实例方法,对静态方法无效。静态方法,只能被隐藏、重载、继承,但不会被重写。
多态总结:
- 多态指的是不同子类型的对象,对同一行为作出的不同响应;
- 实现多态要满足继承/实现、重写、向上转型的条件;
- 多态分为编译时多态和运行时多态,我们常说的多态是指运行时多态;
- 方法重载是编译时多态,方法重写是运行时多态,但重写有例外情况;
- 父类引用指向子类对象时,调用的实例方法是子类重写的方法,父类引用不能调用子类新增的方法和子类特有属性;
- 父类引用指向子类对象时,父类引用只会调用父类自己的属性和static方法,不会调用子类的;
- 多态使得代码更加灵活,方便了代码扩展。