以前一直知道多态是什么东西,平时敲代码也常常用到多态,但一直没有真正了解多态底层的运行机制究竟是怎么样的,这两天才研究明白点,特意写下来,跟各位同窗一块儿进步,同时也但愿各位大神指导和指正。数组
多态的概念:同一操做做用于不一样对象,能够有不一样的解释,有不一样的执行结果,这就是多态,简单来讲就是:父类的引用指向子类对象。下面先看一段代码数据结构
1 package polymorphism; 2 3 class Dance { 4 public void play(){ 5 System.out.println("Dance.play"); 6 } 7 public void play(int i){ 8 System.out.println("Dance.play" + i); 9 } 10 } 11 12 class Latin extends Dance { 13 public void play(){ 14 System.out.println("Latin.play"); 15 } 16 public void play(char c){ 17 System.out.println("Latin.play" + c); 18 } 19 } 20 class Jazz extends Dance { 21 public void play(){ 22 System.out.println("Jazz.play"); 23 } 24 public void play(double d){ 25 System.out.println("Jazz.play" + d); 26 } 27 } 28 public class Test { 29 public void perform(Dance dance){ 30 dance.play(); 31 } 32 public static void main(String[] args){ 33 new Test().perform(new Latin()); // Upcasting 34 } 35 }
执行结果:Latin.play。这个时候你可能会发现perform()方法里面并无相似“if 参数类型 = Dance/Latin”这样的判断,其实这就是多态的特性,它消除了类型之间的耦合关系,令咱们能够把一个对象不当作它所属的特定类型来对待,而是当作其基类的类型来对待。由于上面的Test代码彻底能够这么写:ide
1 public class Test { 2 public void perform(Latin dance){ 3 dance.play(); 4 } 5 public void perform(Jazz dance){ 6 dance.play(); 7 } 8 public static void main(String[] args){ 9 new Test().perform(new Latin()); // Upcasting 10 } 11 }
可是这样你会发现,若是增长更多新的相似perform()或者自Dance导出的新类,就会增长大量的工做,而经过比较就会知道第一处代码会占优点,这正是多态的优势所在,它改善了代码的组织结构和可读性,同时保证了可扩展性。spa
那么到底JVM是怎么指向Latin类中play()的呢?为了解决这个问题,JAVA使用了后期绑定的概念。当向对象发送消息时,在编译阶段,编译器只保证被调用方法的存在,并对调用参数和返回类型进行检查,可是并不知道将被执行的确切代码,被调用的代码直到运行时才能肯定。拿上面代码为例,JAVA在执行后期绑定时,JVM会从方法区中的Latin方法表中取到Latin对象的直接地址,这就是真正执行结果为何是Latin.play的缘由,在解释方法表以前,咱们先了解一下绑定的概念。code
将一个方法调用同一个方法主体关联起来被称做绑定,JAVA中分为前期绑定和后期绑定(动态绑定或运行时绑定),在程序执行以前进行绑定(由编译器和链接程序实现)叫作前期绑定,由于在编译阶段被调用方法的直接地址就已经存储在方法所属类的常量池中了,程序执行时直接调用,具体解释请看最后参考资料地址。后期绑定含义就是在程序运行时根据对象的类型进行绑定,想实现后期绑定,就必须具备某种机制,以便在运行时能判断对象的类型,从而找到对应的方法,简言之就是必须在对象中安置某种“类型信”,JAVA中除了static方法、final方法(private方法属于)以外,其余的方法都是后期绑定。后期绑定会涉及到JVM管理下的一个重要的数据结构——方法表,方法表以数组的形式记录当前类及其全部父类的可见方法字节码在内存中的直接地址。orm
动态绑定具体的调用过程为:对象
1.首先会找到被调用方法所属类的全限定名blog
2.在此类的方法表中寻找被调用方法,若是找到,会将方法表中此方法的索引项记录到常量池中(这个过程叫常量池解析),若是没有,编译失败。索引
3.根据具体实例化的对象找到方法区中此对象的方法表,再找到方法表中的被调用方法,最后经过直接地址找到字节码所在的内存空间。内存
最后说明,域和静态方法都是不具备多态性的,任何的域访问操做都将由编译器解析,所以不是多态的。静态方法是跟类,而并不是单个对象相关联的。对动态绑定还有不明白的请看资料连接,我的感受分析的很到位