多态
定义:
对同一个对象,在不同时刻体现出来的不同状态
JAVA中多态的前提:
- 有继承关系
- 有方法重写
- 有父类引用指向子类对象
Father f = new Son();
多态中成员的访问特点
- 成员变量
- 编译看左边,执行看左边
- 成员方法
- 编译看左边,执行看右边
实例:
Animal类(父类)
public class Animal {
public int age = 40;
public String name = "老虎";
public void eat() {
System.out.println("eat rise");
}
public void play() {
System.out.println("动物喜欢玩");
}
}
Cat类(子类)
public class Cat extends Animal {
public int age = 20;
public int catHeight = 15;
public void eat() {
System.out.println("The cat like eatting cake!");
}
public void climb() {
System.out.println("猫喜欢爬");
}
public void play() {
System.out.println("猫喜欢玩");
}
}
Test类(测试类)
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
System.out.println(a.age);
a.eat();
System.out.println(a.name);
// System.out.println(a.catHeight);
a.play();
// a.climb();
}
}
测试结果:
40
The cat like eatting cake!
老虎
猫喜欢玩
总结
我们发现,当父类引用指向子类对象的时候:
Animal a = new Cat();
对象a调用的成员变量age,打印输出的是父类的age:
System.out.println(a.age);//40
对象a调用的成员变量name,打印输出的是父类的name:
System.out.println(a.name); //老虎
对象a调用的成员变量catHeight,编译报错,说无法在Animal类中找到该成员变量。
System.out.println(a.catHeight); //编译出错
结论
当父类引用指向子类对象的时候,用该对象调用的成员变量必须是父类中含有的,而且执行会打印输出父类的成员变量的值。
- 编译看左边,执行看左边
对象a调用成员方法eat(),打印输出的却是子类的成员方法eat();
a.eat(); //The cat like eatting cake!
对象a调用成员方法play(),打印输出的却是子类的成员方法play();
a.play(); //猫喜欢玩
对象a调用Cat类仅有的成员方法climb(),编译报错:
a.climb(); //报错
结论
当父类引用指向子类对象的时候,用该对象调用的成员方法必须是父类中含有的,而执行的时候,如果子类也有这个成员方法,那么执行的是子类的成员方法。
如果子类中没有,那么执行的还是父类的成员方法。
如果该成员方法只有子类有,而父类没有,在调用该方法后编译会直接报错,无法执行。
- 编译看左边,执行看右边
问题
- 为什么成员变量和成员方法的访问不一样呢?
答:因为成员方法有重写,而成员变量没有。
多态的好处和弊端
- 好处:
- 提高了程序的扩展性
实例
Animal类(父类)
public class Animal {
public void eat() {
System.out.println("吃东西");
}
}
Cat类(子类)
public class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
Dog类(子类)
public class Dog extends Animal{
public void eat() {
System.out.println("狗吃骨头");
}
}
AnimalOperator类(操作类)
public class AnimalOperator {
public void animalOperatorDemo(Animal a) {
a.eat();
}
}
polymorphicDemo类(测试类)
public class polymorphicDemo {
public static void main(String[] args) {
AnimalOperator ao = new AnimalOperator();
Cat c = new Cat();
//父类对象指向子类引用
Animal a = new Cat();
ao.animalOperatorDemo(c);
ao.animalOperatorDemo(a);
//编译通过与否看左边,而方法执行用右边
}
}
打印结果:
猫吃鱼
猫吃鱼
- 弊端:
- 不能使用子类特有的功能
多态转型
向上转型
- 从子到父
Animal a = new Cat();
- 父类引用指向子类对象
向下转型
- 从父到子
Cat c = (Cat) a;
- 父类引用转为子类对象
问题
在多态情况下,要调用子类特有的方法,怎么做?
- 创建子类对象,调用子类对象的方法。
- 采用向下转型,调用子类对象的方法。
实例
Animal类(父类)
public class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
Cat类(子类)
public class Cat extends Animal{
public void eat() {
System.out.println("猫吃鱼");
}
public void play() {
System.out.println("猫玩躲猫猫");
}
}
Dog类(子类)
public class Dog extends Animal{
public void eat() {
System.out.println("狗吃骨头");
}
public void lookDoor() {
System.out.println("狗看门");
}
}
Test类(测试类)
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 向上转型
Animal a = new Cat();
a.eat();
System.out.println("---------");
// 向下转型
Cat c = (Cat) a;
c.eat();
c.play();
// 向上转型
System.out.println("---------");
a = (Animal)c;
a.eat();
// 向下转型错误演示
/*
System.out.println("----------");
Dog d = (Dog)a;
d.eat();
d.lookDoor();
//这样做会出现类型转换异常classcastexception
*/
//向上转型
a = new Dog();
a.eat();
//向下转型
Dog d = (Dog) a;
d.eat();
d.lookDoor();
}
}
打印结果:
猫吃鱼
猫吃鱼
猫玩躲猫猫
猫吃鱼
狗吃骨头
狗吃骨头
狗看门