多态性
多态性的概念
所谓多态性,理解为一个事物的多种形态。具体点就是去完成某个动作时,不同的对象会产生不同的状态。
多态性的好处
多态在Java中指的是父类的引用指向子类的对象,或者可以说是子类的对象赋给父类的引用。这样在我们的实际开发中,就可以使用不同子类的对象赋给相同的父类,在不改变代码结构的情况下,动态的扩展和切换对象的行为,使得代码更高效。
如何实现多态性
简而言之,就是父类的引用指向子类的对象 或者 子类的对象赋给父类的引用。
public class Animal {
}
public class Dog extends Animal{
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
}
}
实现原理
动态绑定
动态绑定,指的是在编译时期只知道变量的声明类型,但是无法确定实际的对象类型。在运行时,JVM会解析实际对象的类型。这意味着,直到运行时,才可以确定调用的实际方法。正是这种动态绑定机制,才使得多态成为可能。
静态绑定:在编译时期就可以确定所要调用的具体类型,且在运行时期保持不变。
虚方法调用
虚方法调用是在运行时根据实际对象的类型来确定要调用哪个方法的机制,而无法在编译时确定具体的调用方法。在多态的情况下,子类重写了父类的A方法,此时A方法就被称为虚方法,在运行时,虚拟机根据实际的子类对象,来动态调用属于子类的A方法。像A方法这样的方法在编译时是无法确定具体调用哪个方法,只有真正运行时才确定。
在多态的场景下,调用方法时:
编译时,认为是左边声明的父类类型的方法;
运行时,认为是右边声明的子类重写的方法。
简称为:编译看左边,运行看右边。
多态性的使用前提
1. 要有类的继承关系。
2. 方法重写,没有方法的重写也可以实现多态性,但是从多态性的现实意义来说,不重写方法的多态性是没有意义。
多态性的好处和弊端
好处
1. 开发中:使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,提高了扩展性。
2. 多态无处不在,在后续的抽象类和接口中,本身是不能定义对象的,只有其的子类才能定义对象,这无疑是多态的体现。
3. 变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活、功能更强大,可维护性和扩展性更好了。
弊端
在多态的场景下,我们创建了子类的对象,在内存中加载了子类特有的方法和属性。但是由于声明为父类的引用,导致我们并没有办法调用子类特有的属性和方法。
多态的适用性
多态适用于方法,不适用于属性。
实现多态之后,如果直接调用属性,发现值是父类的;如果使用方法调用属性,发现值是子类的。其实,这就是静态绑定和动态绑定的问题了。
Java为了实现多态这个机制,选择让方法在运行时绑定对应的类型,属性在编译时期绑定对应的类型。因此,属性绑定的是父类的值,而方法绑定的是子类对象的值。
当直接调用属性时,调用到的是父类的属性,正是由于属性在编译期间绑定,而多态编译期间的变量是父类类型,因此绑定的就是父类的属性值。
当使用方法调用属性时,调用到的是子类的属性,使用super.属性除外。重写方法在运行期间确定对应的地址,也就是动态绑定。而使用子类的方法时,由于默认前边有this.,因此调用的就是子类的属性,这也正好说明了为啥调用super.除外。
所谓多态性,就是一个事物的多种状态,即不同的对象去完成相同的动作时会产生不同的状态。而我认为在实际开发中,多态的体现更多在接口和抽象类中,因为在大多数场景下,其实属性和未重写的方法并没有什么作用。利用抽象类和多态可以让在不修改代码结构的场景下,动态的扩展和切换对象的行为,从而使代码更高效。谈完多态的概念和实际开发场景,多态的实现原理也应该是一个比较重要的内容,其利用动态绑定和虚方法调用的底层实现,从而可以让多态称为现实。
重写属于动态多态,而重载属于静态多态。
向上转型 / 向下转型
向上转型就是多态,而向下转型则是先通过向上转型之后,再向下强转。如下图:
向上转型一般是不存在问题的,而向下转型就不一定了。由于Java的继承是单继承性,只有一个父类,但是一个父类却有很多子类。因此当向下转型时,我们就需要考虑可能会出现类型转换异常。建议在向下转型之前,使用instanceof关键字进行判断,避免出现类型转换异常。instanceof的使用格式是:对象名 instanceof 类名。对于instanceof来说,只要对象对应的类是该类的子类即可。