首页 > 代码库 > 多态的理解
多态的理解
下面从一个简单的例子来说明面向对象的多态机制:
假设,有个妹子养了只宠物,宠物高兴了会叫。
那么首先来看如何抽象宠物这个类:
class Animal { String name; Animal(String name) { this.name = name; } public void enjoy() { System.out.println("叫声......"); }}
然后再来抽象妹子这个类:
class Lady { String name; Animal pet; Lady(String name,Animal pet) { this.name = name; this.pet = pet; } public void myPetEnjoy() { pet.enjoy(); }}
但宠物是个很泛泛的概念,我们希望具体到是猫还是狗,可以从宠物这个类派生出猫和狗这2个子类,而猫和狗的叫声明显是不一样的,可以重写"叫"这个动作,来看代码:
class Cat extends Animal { String eyesColor; Animal(String name,String eyesColor) {
super(name); this.eyesColor = eyesColor; } public void enjoy() { System.out.println("猫叫声......"); }} class Dog extends Animal { String furColor; Dog(String name) {
super(name); this.furColor = furColor; } public void enjoy() { System.out.println("狗叫声......"); }}
然后我们来做个测试,上主函数:
public class Test { public static void main(String[] args) { Cat c = new Cat("Tom"); Dog d = new Dog("Jerry"); Lady l1 = new Lady("lili",c); Lady l2 = new Lady("mimi",d); l1.myPetEnjoy(); l2.myPetEnjoy(); }}
有个叫lili的妹子,养了只叫Tom的猫,还有个叫mimi的妹子,养了只叫Jerry的狗,然后Tom和Jerry在一起高兴的玩耍,我们来听听他们的叫声是否一样呢?自己运行一下就知道了,结果是不一样的。
可为什么呢?
我们看看运行时的内存分配就清楚了。
这里我们仅以猫为例子,狗同理。
可以看到,在new猫的时候,实际先new了一个Animal对象。然后再添加猫自己的成员变量eyesColor。
接下来是重点,虽然pet要求的类型是Animal,而实际给的是Cat对象,可编译器很固执,仍然会把c当做Animal类型看待。所以pet实际是指向了c内部的Animal对象。
然后运行到lili.myPetEnjoy()。
我们知道,无论是否创建、创建多少对象,都只会在代码区存放一份方法代码,而创建对象时也不会添加指向方法的引用,只有调用方法时,才会动态分配一个引用,指向方法的地址。
此时,编译器不再自欺欺人了,客观的认识到这个pet实际类型是Cat,所以会去调用Cat的enjoy方法,而不是Animal的。
也就是说,在调用方法时,编辑器会根据对象的实际类型,动态的判断应该加载哪个方法,这就是多态。也是面对对象最重要的核心。
假设某天妹子烦猫了,又养了只鸟,只需给Anima再添加一个bird子类,然后重写enjoy方法,调用lili.myPetEnjoy方法时,会自动调用bird的enjoy,而不用动其他逻辑,可扩展性是不是很棒!
多态的理解