博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多态学习小记
阅读量:6617 次
发布时间:2019-06-25

本文共 5760 字,大约阅读时间需要 19 分钟。

hot3.png

在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。

    在这篇博文中,通过一些简单的例子来介绍多态,使用多态的好处和带来的缺点,并稍微介绍一下多态在虚拟机中如何实现的。
    多态也称动态绑定,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法.
    多态有两种常用的实现机制,一种是子类继承父类(extends),另外一种是类实现接口(implements).在这里主要介绍第一种.
1.多态的实现及多态在虚拟机如何实现的
先来看一个例子,不使用多态的情况下

package org.felix.polymorphism;class Dog{      String name;      Dog(String name){             this.name = name;      }             public void enjoy(){            System. out.println("wang wang ...." );      }}class Cat{      String name;      Cat(String name){             this.name = name;      }       public void enjoy(){            System. out.println("miao miao ...." );      }}public class Lady {       private String pet ;       public String getPet() {             return pet ;      }       public void setPet(String pet) {             this.pet = pet;      }       public void myPetEnjoy(){             if(pet .equals("Cat")){                   new Cat("Miao" ).enjoy();            } else if (pet .equals("Dog")){                   new Dog("Wang" ).enjoy();            } else{                  System. out.println("the pet is not exits" );            }      }       public static void main(String[] args) {            Lady lady = new Lady();            lady.setPet( "Cat");            lady.myPetEnjoy();      }}
    在这里,我们定义了两个类Cat和Dog,它们都有自己的name属性(动物的名字)和enjoy方法(自己的娱乐方法).
    另外定义一个Lady类,设置这么一个情节,假设这个Lady可以去养一只宠物,这个小动物可能是Cat或者Dog,最后这个Lady类里有个方法myPetEnjoy(),用于调用小动物的enjoy()方法.
    在Lady类中的
myPetEnjoy方法,如果pet是Dog,我们需要调用new Dog().enjoy(),如果pet是Cat,我们需要调用new Cat().enjoy().假设有一天我要传入一个Bird,我还得先判断pet是否是Bird,再来调用new Bird().enjoy()方法.
    新加入什么类型的宠物都需要修改这个方法,这样的程序可扩展性显然是非常糟糕的.

再来看一个例子,这次利用多态的写法,我们可以随意加入任何类型的宠物对象,且程序的可扩展性高
package org.felix.polymorphism;public abstract class Animal {       public abstract void enjoy();}package org.felix.polymorphism;public class Cat extends Animal {       public String name ;            Cat(String name){             this.name = name;      }       @Override       public void enjoy() {            System. out.println("miao miao ..." );      }}package org.felix.polymorphism;public class Dog extends Animal {       public String name ;      Dog(String name){             this.name = name;      }       @Override       public void enjoy() {            System. out.println("wang wang" );      }}package org.felix.polymorphism;public class Lady {       private Animal pet ;       public Lady(Animal pet){             this.pet = pet;      }       public void myPetEnjoy(){             pet.enjoy();      }       public static void main(String[] args) {            Dog dog = new Dog("Wang" );            Lady lady = new Lady(dog);            lady.myPetEnjoy();      }}
     为Cat和Dog创建一个抽象的Animal类,它有一个抽象的enjoy的方法,它不管它的导出类是如何实现它的.利用动态绑定,我们在new出一个对象时,程序会为我们自动的去调用响应的enjoy()方法.如果我们需要扩展一个Bird类,我们只要让Bird类继承自Animal,并实现相应的enjoy()方法即可.

借助上图我们来分析一下动态绑定如何在虚拟机中执行的.
    首先,在栈空间分配一块空间给dog变量,在堆空间中生成一个Dog对象,在Dog对象中有一属性name,值为"dog",Dog对象中的enjoy方法存放在
方法区中.栈空间中的dog变量指向堆空间中的Dog变量.
    其次,在栈空间中分配一块空间给lady变量,在堆空间中生成一个Lady对象,lady指向Lady对象.Lady对象中有一属性pet,类型为Animal,值为dog.由于向上转型,pet指向Dog对象中的Animal.
    最后,当我们执行lady.myPetEnjoy()时,由于动态绑定,就能正确的执行方法区中属于Dog的enjoy()方法,而不是其他对象的enjoy()方法.实际上,Animal中存在一个指向它的导出类中的enjoy()方法的指针,根据所new出来的导出类,动态的改变指针指向.这就是动态绑定.

2.多态存在的三个必要条件

  • 要有继承
  • 要有重写
  • 父类引用调用子类对象

3.多态的一些缺陷

3.1不能覆盖私有方法

package org.felix.polymorphism;public class PrivateOverride{       private void f(){            System. out.println("private f()" );      }       public static void main(String[] args) {            PrivateOverride po = new Derived();            po.f();      }}class Derived extends PrivateOverride{       public void f(){            System. out.println("public f()" );      }}
    我们期望它输出的是public f(),但实际上输出的是private f().
    为什么呢?因为private方法默认是final的,且对导出类即
Derived
 是屏蔽的.因此,在这种情况下,
Derived
 
    结论就是:只有非private方法才可以被覆盖.
PrivateOverride中的f()改为protected或者public,就可以得到自己想要的结果.因为多态.
    一个建议:在导出类中,对于基类中的private方法,最好采用不同的名字.
3.2
域不具有多态性
package org.felix.polymorphism;class Super{       public int field = 0;       public int getField(){return field;}}class Sub extends Super{       public int field = 1;       public int getField(){return field;}       public int getSuperField(){return super.field ;}}public class FieldAccess {       public static void main(String[] args) {            Super sup = new Sub();            System. out.println(sup.field ); //output 0            System. out.println(sup.getField());//output 1            Sub sub = new Sub();            System. out.println(sub.field ); //output 1            System. out.println(sub.getField()); //output 1            System. out.println(sub.getSuperField());//output 0      }}
    当Sub对象转型为Super引用时,任何域访问操作都将由编译器解析,因此不是多态的.(多态是在运行过程中进行动态绑定).
    在这个例子中,虚拟机为Super.field和Sub.field分配了不同的存储空间(在堆内存中).这样,Sub实际上包含两个称为field的域.
    然而,在引用Sub中的field时所产生的默认域并非Super版本的域。因此,为了得到Super.field,必须显示的指明super.field.
3.3静态方法不具有多态性
package org.felix.polymorphism;class StaticSuper{       public static String staticGet(){             return "Super staticGet()" ;      }       public String dynamicGet(){             return "Super dynamicGet" ;      }}class StaticSub extends StaticSuper{       public static String staticGet(){             return "Sub staticGet()" ;      }       public String dynamicGet(){             return "Sub dynamicGet" ;      }}public class StaticPolymorphism {       public static void main(String[] args) {            StaticSuper sup = new StaticSub();            System. out.println(sup.dynamicGet());            System. out.println(sup.staticGet ());      }}
如果某个方法是静态的,它的行为就不具有多态性.
静态方法是与类,而并非与单个的对象的相关联的.(画个内存图就明了)
3.4小结
   域不具有多态性,如果你直接访问某个域,这个访问就在编译期进行解析了.
   除了static和final方法之外,(private 属于final型的),其他所有的方法(普通方法)都是后期动态绑定.
--------------------------------------end-------------------------------------------

参考文献 《java编程思想第四版》第八章

转载于:https://my.oschina.net/linuxfelix/blog/124101

你可能感兴趣的文章
hdu Collect More Jewels
查看>>
POJ2390 Bank Interest【水题】
查看>>
CCF201512-4 送货(100分)
查看>>
[转]十年前的老文:以 Linux 的名义
查看>>
jmeter脚本录制的两种方式
查看>>
程序的测试
查看>>
mysql截取longblob类型字段内一小块数据的方法
查看>>
jquery轻松操作CSS样式
查看>>
5.完整网站开发
查看>>
使用msf对tomcat测试
查看>>
CSS3边框
查看>>
Spring事务隔离级别,事务传播行为
查看>>
CRC是什么?
查看>>
容器和物理机的文件共享
查看>>
[硬件]_ELVE_VS2015下opencv3.3的配置问题
查看>>
每次看完羽毛球赛
查看>>
Storm介绍及核心组件和编程模型
查看>>
在android studio中配置运行时签名
查看>>
第一章练习题
查看>>
三、JVM垃圾回收1(如何寻找垃圾?)
查看>>