多态概述
多态是面向对象三大特征之一,是同一个对象,在不同时刻表现出来的不同形态的能力。
举个例子:二哈;
我们可以说二哈是狗:Dog erha = new Dog();
我们也可以说二哈是动物:Animal erha = new Animal();
同样这只二哈,在不同的时刻表现出来了不同的形态,这就是多态。
多态的三个必要条件:
- 继承
- 子类重写父类方法
- 有父类引用指向子类对象
怎么理解这三个条件呢,直接上代码:
// 动物类
class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
// 狗狗类(继承了动物类)
class Dog extends Animal {
// 重写父类eat()方法
@Override
public void eat() {
System.out.println("狗狗吃骨头");
}
}
// Demo类
public class PolymorphismDemo {
public static void main(String[] args) {
// 父类引用指向子类对象
Animal erha = new Dog();
erha.eat();
}
}
最后的运行结果是狗狗吃骨头
,三个条件我在注释中也都标注了出来。这就是最基本的一个多态案例。
多态中成员访问特点
- 成员变量:编译看左边,执行看左边
Animal erha = new Dog();
System.out.println(erha.name); // erha.name只能取到父类Animal中的值
- 成员方法:编译看左边,执行看右边
Animal erha = new Dog();
erha.eat(); // erha.eat()调用的是子类Dog重写后的方法
怎么理解这两句话呢,直接上代码:
// 动物类
class Animal {
public String name = "动物";
public void eat() {
System.out.println("动物吃东西");
}
}
// 狗狗类
class Dog extends Animal {
public String name = "狗狗";
@Override
public void eat() {
System.out.println("狗狗吃骨头");
}
}
// Demo类
public class PolymorphismDemo2 {
public static void main(String[] args) {
Animal erha = new Dog();
System.out.println(erha.name);
erha.eat();
}
}
最后的运行结果是:
动物
狗狗吃骨头
为什么成员变量和成员方法的访问不一样呢?
因为成员方法有重写,eat()
被子类重写了;而成员变量没有重写,访问的依然是父类的name
。
多态的弊端
不能使用子类的特有功能,举个例子
// 动物类
class Animal {
public String name = "动物";
public void eat() {
System.out.println("动物吃东西");
}
}
// 狗狗类
class Dog extends Animal {
public String name = "狗狗";
public int age = 5;
@Override
public void eat() {
System.out.println("狗狗吃骨头");
}
public void bark(){
System.out.println("狗狗汪汪叫");
}
}
// Demo类
public class PolymorphismDemo3 {
public static void main(String[] args) {
Animal erha = new Dog();
System.out.println(erha.name);
erha.eat();
//System.out.println(erha.age); //ERROR
//erha.bark(); //ERROR
}
}
因为这个erha
是父类引用(Animal)指向子类对象(Dog)所创建出来的,子类的特有功能age
与bark()
这边都没法获取,所以这边两行都会报错。
多态中的转型
- 向上转型:父类引用指向子类对象
- 向下转型:父类引用转为子类对象
Animal a = new Dog(); // 向上转型,父类引用指向子类对象
Dog d = (Dog) a; // 向下转型,父类引用被强制转换为子类对象
在代码中实现:
// 动物类
class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
// 狗狗类
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗狗吃骨头");
}
public void bark(){
System.out.println("狗狗汪汪叫");
}
}
// Demo类
public class PolymorphismDemo4 {
public static void main(String[] args) {
Animal erha = new Dog(); // 向上转型
erha.eat();
//erha.bark(); //ERROR
Dog xiaoha = (Dog) erha; // 向下转型
xiaoha.eat();
xiaoha.bark();
}
}
最后的运行结果是:
狗狗吃骨头
狗狗吃骨头
狗狗汪汪叫
在向上转型中,由于是父类引用指向子类对象,因此无法调用bark()
方法,而在向下转型中,父类引用转为子类对象,因此可以调用子类的bark()方法。