一、什么是多态?
1.定义
多态顾名思义即为多种形态的意思。Java多态是面向对象编程的一个重要特性,它允许不同的对象对同一消息做出不同的响应。具体点就是不同的对象去完成相同的一个任务完成后展现的结果不相同。
举例,体育生是一个对象,文化生是一个对象,两个对象完成学习这一件事情得到的结果不相同,这是一个多态现象的体现,但想要在程序世界实现多态就需要其他的特性的辅助了。
2.多态的含义
发送消息给某个对象,让这个对象自行决定采用哪种行为响应这个消息。
子类对象的引用赋值给父类引用变量来实现动态的方法调用。
3.实现多态的前提
- 必须要含有父子类关系 → 继承
- 子类中要重写父类的方法 → 父类方法的重写
- 通过父类的引用调用重写的方法 → 向上转型
宏观理解:
比如我们,是人,也可以扮演很多种角色,这样我们可以通过不同的形态去做不同的事情。但注意:多态前提条件:必须有子父类关系。在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
多态的体现:在代码允许的时候,当传递不同的对象时,会调用对应类中的方法。
4.使用格式
父类类型 变量名=new 子类类型();
5.多态中成员的特点
多态成员变量:编译运行看左边
多态成员方法:编译看左边,运行看右边
二、多态的转型
多态的转型分为向上转型和向下转型两种
1.向上转型
含义:创建一个子类对象,将其当做父类对象来使用。多态本身就是向上转型过的过程。
使用场景有三个:
直接赋值,方法传递,方法返回。
使用格式:父类类型 变量名=new 子类类型();
适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。
优点:让代码变得更加灵活。
缺点:该引用无法调用子类的特有的方法(因为引用是父类类型的,引用本身就只能调用本类型的方法变量,无法调用子类的方法变量)。
//例子:
public class Person {
//父类 人类
public void say(){
System.out.println("人类");
}
}
//子类
class Student extends Person{
@Override
public void say() {
System.out.println("我是人类的学生");
}
}
//子类
class Child extends Person{
@Override
public void say() {
System.out.println("我是人类的孩子");
}
}
//测试类
class Test{
public static void main(String[] args) {
//父类类型 变量名=new 子类类型();
Person per = new Student(); //子类对象的引用赋值给父类
per.say(); //
Person per1 = new Child();
per1.say();
}
}
//这段代码用到了 继承,重写,向上转型,因为多态本来就是向上转型的过程
输出结果:
2.向下转型
含义:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型。
使用格式:子类类型 变量名=(子类类型) 父类类型的变量;
适用场景:当要使用子类特有功能时。
注意事项:当向上转型时,父类引用指向子类对象。当向下转型时,该父类引用只能强转为子类引用,不可以将父类引用强转为子类引用,否则会编译报错。
优点:当我们需要调用子类的方法或者访问子类的变量时,可将父类引用强制转换为子类引用才可访问子类变量。
缺点:一般向下转型使用比较少,因为涉及到强制类型转换,强转是不安全的,有可能会导致数据的丢失,这个是我们无法预知的,因此不到迫不得已不要出现强转的代码。
//例子:
public class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal{
public Dog(String name, int age) {
super(name, age);
}
public void bark(){
System.out.println("会叫的狗狗");
}
}
class Cat extends Animal{
public Cat(String name, int age) {
super(name, age);
}
public void eat(){
System.out.println("猫猫在吃猫粮");
}
}
class TestDome{
public static void main(String[] args) {
Animal animal = new Dog("圆圆",2);
Dog dog = (Dog) animal;
dog.bark();
//如果animal引用的对象是Cat对象的实例
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.eat();
}else{
System.out.println("error");
}
//但是若是Cat cat = (Cat)animal;这个就不可以,因为还原错误,animal指的是Dog类,不能还原为Cat类
}
}
输出结果:
这里引出了instanceof关键字,instanceof用于判断某个对象是否是某个类的实例。
例如: a instanceof B 若a是B的实例,则返回true,否则返回的是false。
三、多态的优缺点
优点:
- 能够降低圈复杂度,避免使用大量的 if-else
- 提高代码的可重用性(可扩展性)
- 降低模块之间的耦合度
什么叫 “圈复杂度” ?
圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解。就是如果有很多的条件分支或者循环语句, 就认为理解起来更复杂。
因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 “圈复杂度”。
如果一个方法的圈复杂度太高, 就需要考虑重构。
不同公司对于代码的圈复杂度的规范不一样, 一般不会超过 10。
缺点:
- 代码的运行效率降低
(1)属性没有多态性
当父类和子类同时有同名属性的时候,通过父类引用,只能调用父类的成员属性。
(2)构造方法没有多态性
动态绑定:又称为运行时绑定,在程序编译的时候不能确定方法的行为(结果),只有当程序运行的时候,才能够确定具体调用哪个类的方法。典型代表是方法重写,又称后期绑定。
静态绑定:在编译的时候编译器通过用户传递的实参类型就可以确定具体调用哪个方法。典型代表是方法重载,又称前期绑定。
四、多态机制
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒 底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的 方法,必须在由程序运行期间才能决定。
因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而 导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时 所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。 多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的 重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不 同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来 实的,也就是我们所说的多态性。