一、多态概述
面向对象编程有三大特性:封装、继承、多态
封装是隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别;继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力;今天我将揭开多态神秘的面纱。
1.通俗理解:多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作,顾名思义即多种形态,即一种事物可以有多种表现的形态,比如黑白打印机和彩色打印机都是打印机,打印出来的分别有彩色和黑白;
再比如,对于我们的计算机来说,按下F1这个键
动作 | 界面 | 弹出 |
按下F1键 | Flash | AS 3的帮助文档 |
Word | Word帮助 | |
Windows | Windows帮助和支持 |
同一个事件发生在不同的对象下会产生不同的结果
2.多态体现为父类引用变量可以指向子类对象
3.多态存在的三个必要条件:①要有继承
②要有方法的重写
③父类引用指向子类对象Parent p = new Child();
(多态的定义与使用格式如上:Parent为父类名称,p为变量名,Child为子类名称)
注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
二、多态中成员的特点
1.多态中成员函数的特点:
①在编译时,参考引用型变更所属的类是否有调用方法,如果有编译通过,如果没有编译不通过
②在运行时,参考实际生成实例的类中是否有调用的方法
2.多态中成员变量(非静态)的特点:无论编译和运行,都参考引用变量所属的类
3.在多态中,静态成员(变量和函数)的特点:无论编译和运行都参考引用变量所属的类
instanceof 关键字用法
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
以下实例创建了 displayObjectClass() 方法来演示 Java instanceof 关键字用法:
/*
author by runoob.com
Main.java
*/
import java.util.ArrayList;
import java.util.Vector;
public class Main {
public static void main(String[] args) {
Object testObject = new ArrayList();
displayObjectClass(testObject);
}
public static void displayObjectClass(Object o) {
if (o instanceof Vector)
System.out.println("对象是 java.util.Vector 类的实例");
else if (o instanceof ArrayList)
System.out.println("对象是 java.util.ArrayList 类的实例");
else
System.out.println("对象是 " + o.getClass() + " 类的实例");
}
}
三、多态的转型
1.向上转型
子类引用的对象转换为父类类型称为向上转型。通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口。
public class Animal {
public void eat(){
System.out.println("animal eatting...");
}
}
public class Cat extends Animal{
public void eat(){
System.out.println("我吃鱼");
}
}
public class Dog extends Animal{
public void eat(){
System.out.println("我吃骨头");
}
public void run(){
System.out.println("我会跑");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Cat(); //向上转型
animal.eat();
animal = new Dog();
animal.eat();
}
}
//结果:
//我吃鱼
//我吃骨头
这就是向上转型,Animal animal = new Cat(); 将子类对象 Cat 转化为父类对象 Animal。这个时候 animal 这个引用调用的方法是子类方法。
转型过程中需要注意的问题
- 向上转型时,子类单独定义的方法会丢失。比如上面Dog类中定义的run方法,当animal引用指向Dog类实例时是访问不到run方法的,
animal.run()
会报错。 - 子类引用不能指向父类对象。
Cat c = (Cat)new Animal()
这样是不行的。
向上转型的好处
- 减少重复代码,使代码变得简洁。
- 提高系统扩展性。
使用格式:父类类型 变量名=new 子类类型();
适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。
2.向下转型
与向上转型相对应的就是向下转型了。向下转型是把父类对象转为子类对象。(请注意!这里是有坑的。)
//还是上面的animal和cat dog
Animal a = new Cat();
Cat c = ((Cat) a);
c.eat();
//输出 我吃鱼
Dog d = ((Dog) a);
d.eat();
// 报错 : java.lang.ClassCastException:com.chengfan.animal.Cat cannot be cast to com.chengfan.animal.Dog
Animal a1 = new Animal();
Cat c1 = ((Cat) a1);
c1.eat();
// 报错 : java.lang.ClassCastException:com.chengfan.animal.Animal cannot be cast to com.chengfan.animal.Cat
为什么第一段代码不报错呢?相比你也知道了,因为 a 本身就是 Cat 对象,所以它理所当然的可以向下转型为 Cat,也理所当然的不能转为 Dog,你见过一条狗突然就变成一只猫这种操蛋现象?
而 a1 为 Animal 对象,它也不能被向下转型为任何子类对象。比如你去考古,发现了一个新生物,知道它是一种动物,但是你不能直接说,啊,它是猫,或者说它是狗。
向下转型注意事项
- 向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
-
向下转型只能转型为本类对象(猫是不能变成狗的)。
使用格式:子类类型 变量名=(子类类型) 父类类型的变量;
适用场景:当要使用子类特有功能时。
虚函数
虚函数的存在是为了多态。
Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。
四、多态的实现方式
1.重写:子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。
2.接口:
① 生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这个是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。
② java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。具体可以看我写的另外一篇关于接口的博客
3.抽象类和抽象方法
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
五、多态实例
public class TestPolymorphism {
public static void animalSaySomething(Animals an) {
an.saySomething();
if(an instanceof Cat) { //判断形参an是不是和cat一样
Cat cat=(Cat)an; //把an强转为cat类型
cat.show();
}
}
public static void main(String[] args) {
Dog dog =new Dog();
Cat cat =new Cat();
Bird bird =new Bird();
animalSaySomething(dog); //形参名(实参);
animalSaySomething(cat);
animalSaySomething(bird);
}
}
class Animals{
public void saySomething() {
System.out.println("哈哈哈哈哈");
}
}
class Dog extends Animals {
public void saySomething() {
System.out.println("嘿嘿嘿嘿嘿");
}
}
class Cat extends Animals{
public void saySomething() {
System.out.println("吼吼吼吼吼");
}
public void show() {
System.out.println("啊啊啊啊啊");
}
}
class Bird extends Animals{
public void saySomething() {
System.out.println("嘻嘻嘻嘻嘻");
}
}
哈哈哈哈哈
嘿嘿嘿嘿嘿
吼吼吼吼吼
啊啊啊啊啊
嘻嘻嘻嘻嘻
六、多态的优点
1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性