首页 > 代码库 > 多态的理解

多态的理解

下面从一个简单的例子来说明面向对象的多态机制:

假设,有个妹子养了只宠物,宠物高兴了会叫。

那么首先来看如何抽象宠物这个类:

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,而不用动其他逻辑,可扩展性是不是很棒!

 

多态的理解