多态的概述
同一个对象,在不同时刻表现出来的不同形态
举例:
多态的前提和体现
1有继承/实现关系
2有方法重写
3有父类引用指向子类对象
多态中成员的访问特点
创建Animal类
public class Animal {
public int age = 40;
public void eat(){
System.out.println("动物吃东西");
}
}
创建cat类
public class Cat extends Animal{
public int age = 20;
public int weight = 20;
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame(){
System.out.println("躲猫猫");
}
}
创建animaldemo类
public class AnimalDemo {
public static void main(String[] args) {
//父类引用指向子类对象
Animal animal = new Cat();
System.out.println(animal.age);
// System.out.println(animal.weight);
animal.eat();
// 报错 animal.playGame();
}
}
执行结果
"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe" "-javaagent:D:\IntelliJ IDEA com.itheima_05.AnimalDemo
40
猫吃鱼
进程已结束,退出代码0
总结:成员变量: 编译看左边,执行看右边
成员方法,编译看左边,执行看右边
其实都可以看左边,方法有重写看重写
多态的好处和弊端
好处:提高了程序的扩展性
具体体现:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作
弊端:如果父类没有该方法,子类没有重写,则不能使用子类的特有的功能
多态中的转型:
向上转型:
从子到父
父类引用指向子类对象
向下转型:
从父到子
父类引用转为子类对象
创建Animal类
public class Animal {
public void eat(){
System.out.println("吃东西");
}
}
创建Cat类
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("玩游戏");
}
}
创建AnimalDemo类
public class AnimalDemo {
public static void main(String[] args) {
//多态 向上转型
Animal animal = new Cat();
// Cat cat = new Cat();
// cat.eat();
// cat.playGame();
//向下转型
Cat cat1=(Cat) animal; //父类对象强制转换成子类对象 就是向下转型
cat1.eat();
cat1.playGame();
}
}
特别说明:
Animal animal = new Cat();
animal.eat();编译可以通过是因为animal中有eat方法,执行的时候则是看Cat中被重写过的eat方法;
编译看左边,执行看右边
这样是可以通过animal来调用cat的eat方法了,但是我们要使用cat特有的方法的时候,就要再创建一个cat对象,这样一来就创建了两个cat对象,开辟了两次空间,非常浪费
这个时候我们可以使用向下转型,在向上转型的基础之上,将animal对象强制转换成Cat对象
即Cat cat1=(Cat) animal
这样左右两边都是Cat类,自然可以调用Cat特有的方法,play games()。
一般实际工作中person都会是一个接口,而接口不能new对象,就无法调用方法,这时候就要向上转型,再调用方法,而且只能调用接口里的方法,而不能调用实现类方法。
内存图详解
1程序开始,main方法先被加载到栈内存,
然后Animal a 被加载到main方法中,
new Cat在堆内存中开辟空间,地址值为001,并把地址值给Animal a
到这里是多态的形式,向上转型
在多态中,编译看左边,执行看右边
这时候就要调用Cat中的eat方法,所以Cat类和方法eat被加载到栈内存中
方法调用完毕,Cat类和eat方法消失
Cat c 被加载到main方法中
因为这时a的地址值原本就是001,指向地址值为001的cat
这时我们吧001 也给到 Cat c
这时调用方法就相当于调用001 Cat中的
这时我们向上转型狗 dog 两者为继承关系,所以成立,在堆内存空间中创建一个new Dog() 地址值为002的空间 同时a的地址值变为002
编译看左边,执行看右边,右边是Dog,所以去Dog中找eat方法,输出
这时我们再向下转型,这时候a 的地址值002 指向 狗,左边是猫,右边是狗,
虽然猫狗都继承自动物,但是彼此并不相等,如果强制执行,就会产生ClassCastException(类型转换异常)