这篇文章主要介绍面向对象三大特性中的多态性,内容主要包括:
- 什么是多态
- 多态的实现
- 向下转型与instanceof操作符
什么是多态
多态这个词的字面意思就是多种形态,例如在实际的生活中一个大学生jone,他在校园内是一个学生,可以享受学校的政策,但他同时也是一个人,需要受到宪法制约。jone有学生和人两种形态,jone具体表现为那种形态需要根据jone所处的环境而定了。
这个属性在操作电脑的快捷键时也得到了体现。我们平时在一个软件中按ctrl+s保存,保存的时当前正在操作的软件的文件,而不是其他程序中的文件。
在Java中也是这样,程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。即如果在创建一个父类的对象,在编译阶段会表现为这个父类,但是在程序运行的时候可能运行的是它的某一个子类。
这样做的好处就是可以增加代码的复用性,减少子类之间的耦合,同时也使得编程更加的灵活。
多态的使用
下面这条语句表现了java中多态性的常见使用:
父类 变量名 = new 子类()
例如
1 | animal dog = new Dog(); |
这时父类的引用指向的子类的对象(animal类型的引用指向了它的子类Dog),也就是说,在编阶段Java依然会认为dog是一个animal类,但是在实际运行的时候将运行的是Dog类中重写的方法,这就表现了java的动态绑定特性。这时调用方法称为虚拟方法调用。
在使用多态性或者说虚拟方法调用时,需要注意以下几点:
- 需要有类的继承关系
- 要有方法的重写
- 多态性只适用于方法,不适用于属性
在Java中方法调用的优先级为:
- this.show(O)
- super.show(O)
- this.show((super)O)
- super.show((super)O)
即优先在自己的子类中查找符合调用条件的方法,如果没有就在自己的超类中找,如果依然没有就将形参引用提升为其父类,在进行查找。
经典例题
接下来看一个网上比较经典的例子:
1 | //假设又以下的类及其之间的关系 |
进行实例化
1 | A a1 = new A(); |
以下输出的时什么
1 | System.out.println(a1.show(b)); |
1 | System.out.println(a1.show(c)); |
1 | System.out.println(a1.show(d)); |
1 | System.out.println(a2.show(b)); |
1 | System.out.println(a2.show(c)); |
1 | System.out.println(a2.show(d)); |
向下转型
再多态的概念中提到,虽然在多态性中内存中加载了子类的属性和方法,但是由于变量声明为父类类型,导致编译时只能调用父类的属性和已经被子类重写方法。
为了解决这个问题,我们可以将相应的引用强制转换成其子类。格式如下:
子类 变量名 = (子类)父类变量名
这就是将一个超类直接指定为他的某一个子类,也就是向下转型。(多态可以理解为向上转型)
但是如果直接这这样写会带来一些问题:
假设Person类是Man类和Women类的超类
例一:
1 | //编译可以通过,但不能成功运行 |
例二:
1 | //编译和运行都能通过,但是当调用时会出问题 |
正确的转换因该如下
1 | Person p1 = new Man(); |
为了避免出现上面的问题,一般在进行强制转换前用instanceof判断一下
instanceof运算符的前一个操作符是一个引用变量,后一个操作数通常是一个类(可以是接口),用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是返回true,否则返回false。
1 | Person p1 = new Person(); |
本文链接: https://quandongli.github.io/post/6eaacac6.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!
