精华内容
下载资源
问答
  • 这篇文章我们将揭露JVM方法调用的静态(static binding) 和动态绑定机制(auto binding) 。  静态绑定机制 //被调用的类 package hr.test; class Father{ public static void f1(){ System.out.println(...
  • JAVA动态绑定机制

    2016-05-10 11:09:43
    一个方法可能在几个子类中实现,JAVA虚拟机在运行时动态绑定方法的实现,由变量引用的对象的实际类决定。 动态绑定的工作机制如下:假设对象o是类c1,c2.......cn的实例,其中c1是c2的子类,c2是c3的子

    子类方法对父类方法的覆盖以及一般方法的重载都可以看作是多态。

    动态绑定是一种机制。引用变量的声明类型决定了编译时哪个方法匹配;编译时,编译器根据参数类型、参数个数和参数顺序匹配方法。一个方法可能在几个子类中实现,JAVA虚拟机在运行时动态绑定方法的实现,由变量引用的对象的实际类决定。

    动态绑定的工作机制如下:假设对象o是类c1,c2.......cn的实例,其中c1是c2的子类,c2是c3的子类......cn-1是cn的子类,也就是说cn是最一般的类,c1是最特殊的类。如果对象o调用一个方法p,JAVA虚拟机依此在类c1,c2.......cn中找方法P的第一个实现。

    package com.mypackage.extend;
    public class Animal {
       String type;
       String name;
       int age;
       int weight;
       void eat()
       {
      System.out.println("动物爱吃饭");
       }
       
       void breath()
       {
      System.out.println("动物呼吸");
       }
       
       void sleep(){
      System.out.println("动物睡觉");
       }
    }

    class Tiger extends Animal
    {
    String tigerType;
    String from;
    @Override
    void breath() {
    // TODO Auto-generated method stub
    //super.breath();
    System.out.println("老虎用肺呼吸");
    }

    void tigerRun()
    {
    System.out.println("老虎在奔跑");
    }
    }


    不能通过数组来直接访问Tiger类自己的方法,必须要进行类型强制转换。因为动态绑定只对子类覆盖父类的方法有效,其他一般方法调用由声明的类型决定。使用A instanceof  B用来判断A是否是B的一个实例,防止ClassCastException出现。


    public class TestAnimal {
    /**
    * @param args
    */
    public static void main(String[] args) {
    // TODO Auto-generated method stub
         Animal[] animal = new Animal[2];
         animal[0] = new Animal();
         animal[1] = new Tiger();
         
         animal[0].breath();
         animal[1].breath();
        
         if(animal[1] instanceof Tiger)
        ((Tiger)animal[1]).tigerRun();
    }
    }

    展开全文
  • JAVA多态的动态绑定机制

    千次阅读 2018-10-19 17:27:51
    对于多态,大家基本上都很熟悉,日常开发用的也挺多,一句话概括:父类引用指向...为了弄清楚多态,我们需要引入jvm方法调用的静态和动态绑定机制的概念, jvm静态绑定机制 Public class Utils{ private stati...

    对于多态,大家基本上都很熟悉,日常开发用的也挺多,一句话概括:父类引用指向子类对象

    • 在集合的使用上,List mList = new ArrayList<>();
    • 在类的继承时,Anim anim = new Cat();
      为了弄清楚多态,我们需要引入jvm方法调用的静态和动态绑定机制的概念,
      jvm静态绑定机制
    Public class Utils{
    	private static Utils utils;
    	private Utils(){}
    	Public static Utils getInstace(){
    		utils = new utils();
    		return utils ;
    	}
    }
    class testActivity extends Activity{
    	@Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(setView());
            Utils.getInstace();//静态绑定就在这里
        }
    }
    

    静态绑定的这句代码Utils.getInstance(),会被编译器编译成一条指令:invokstatic #13,jvm是如何处理这条指令的?
    (1) 指令 #13的含义:testActivity 类常量池中第13个常量表的索引项,关于常量池详见《Class文件内容及常量池 》,这个常量表记录的是方法getInstance()信息的符号引用,包括getInstance所在的类名、方法名和返回类型,jvm首先会根据这个符号引用去找到getInstance方法所在类的全包名
    (2)紧接着jvm会加载、链接、初始化Utils类
    (3)然后再testActivity 类所在的方法区找到getInstance()方法的直接地址,且将这个直接地址记录到testActivity 类常量池中索引为13的常量表,这个过程称为常量池解析,以后再次调用Utils.genInstance()时,直接找到方法的字节码
    (4)完成了类的常量池索引项中常量表解析后,jvm就能调用这个静态方法,去执行方法逻辑
    通过上述几个步骤后,我们发现当完成常量池解析后,jvm就能确定getInstance方法具体在内存的什么位置上,事实上这个信息在编译阶段就已经在类的常量池中记录了下来,在编译阶段能够确定方法在内存什么位置的机制就叫静态绑定机制
    *所有私有方法、静态方法、构造器及final修饰方法都是采用静态绑定机制。在编译器阶段就已经指明了调用方法在常量池中的符号引用,JVM运行的时候只需要进行一次常量池解析即可 *。

    jvm动态绑定机制

    public class AnimObj {
        private  String name = "anim";
        public  String f1() {
            Log.e("11111111_Anim_getName","Anim_getName");
            return name;
        }
    }
    //子类
    public class DogObj extends AnimObj {
          String name = "dog";
        public  String f1() {
            Log.e("11111111_dog_getName","Dog_getName");
            return name;
        }
    }
    //多态的调用,
    AnimObj anim = new DogObj();
            anim.f1();
    

    首先我们知道,要有多态,它的前提就是继承、重写,那么jvm是如何确保找到的方法是cat 类中而不是Anim类?有点绕口,就是说代码中类的引用是Anim,正常情况下调用的方法也是Anim类中的方法,多态是怎么做到把这个方法变成调用Cat类中的方法?在这里我们先要讲一下jvm管理的一个非常重要的数据结构-----方法表
    在jvm加载的同时,会在方法区中为这个类存放很多信息(详见《Java 虚拟机体系结构 》),其中有一个数据结构叫方法表,它以数组的形式记录了当前类和所有父类的可见方法字节码在内存中的直接地址,下图是对比的两个方法表
    在这里插入图片描述
    上图中的方法表有两个特点:(1) 子类方法表中继承了父类的方法,比如Anim extends Object。 (2) 相同的方法(相同的方法签名:方法名和参数列表)在所有类的方法表中的索引相同。比如Father方法表中的f1()和Sun方法表中的f1()都位于各自方法表的第11项中。
    对于上面的源代码,编译器会将多态调用的方法翻译成字节码指令

    //(1) Anim a =  new Cat();  //在堆内存中开辟一个Cat对象的内存空间,并将对象引用压入操作栈
    //invokespecial #7 [15]  //调用初始化方法初始化堆中的Cat对象
    //astore_1 //弹出操作树栈的Cat对象引用压入局部变量表1中
    // aload_1 //取出局部变量表1的对象引用压入操作数栈
    // invokevirtual #15   调用getName方法
    

    其中invokevirtual 的详细指令是这样的:

    1. invokevirtual #15中的 #15指TestActivity类中常量池第15位置的常量表索引项,这个常量表记录的是方法f1信息的符号引用(包括 类名及其所有父类、方法名和返回类型),JVM首先会根据方法的符号引用找到调用f1方法的类的全包名(全限定名)com.my_project.internal_obj.obj.AnimObj,因为这是调用f1方法的类对象anim声明为Anim类型。
    2. 在Anim类型的方法表中查找f1方法,如果找到,将f1在方法表中的索引项11记录到TestActivity类常量池第15位置的常量表中(也就是常量池解析),需要引起注意的是,如果f1这个方法在Anim类中没有,即使Cat方法中存在,也无法通过编译,因为当前类的类型是Anim
    3. 在调用invokevirtual指令前有一个aload_1指令,它会将创建在堆内存中的Cat对象引用压入操作数栈,然后invokevirtual指令会根据Cat的对象引用首先找到堆内存中的Son对象,进一步找到Cat对象所属类型的方法表,下图所示:
      在这里插入图片描述
      方法数据存放在类的方法区中,包含一个方法的具体实现的字节码二进制。方法指针直接指向这个方法在内存中的起始位置,通过方法指针就可以找到这个方法。
    4. 这是通过第二步解析完成的#15常量表中方法表的索引项11,可以定位到Son类型方法表中的f1方法,然后通过直接地址找到该方法字节码所在的内存空间

    类对象方法的调用必须在运行过程中采用动态绑定机制。
    首先,根据对象的声明类型(对象引用的类型)找到“合适”的方法。具体步骤如下:
    ① 如果能在声明类型中匹配到方法签名完全一样(参数类型一致)的方法,那么这个方法是最合适的。
    ② 在第①条不能满足的情况下,寻找可以“凑合”的方法。标准就是通过将参数类型进行自动转型之后再进行匹配。如果匹配到多个自动转型后的方法签名f(A)和f(B),则用下面的标准来确定合适的方法:传递给f(A)方法的参数都可以传递给f(B),则f(A)最合适。反之f(B)最合适 。
    ③ 如果仍然在声明类型中找不到“合适”的方法,则编译阶段就无法通过。
    然后,根据在堆中创建对象的实际类型找到对应的方法表,从中确定具体的方法在内存中的位置。

    这是我自己理解的,不知道对不对!!
    在编译期间,Anim对象类型调用的f1方法,是将方法表中的索引项11是指向Anim这个对象类型的,而在运行期间,Anim对象类型在堆内存中创建的实际对象是Cat,所以jvm会根据内存中真实的对象引用重新去给f1的方法表索引项11赋值,这种通过程序运行过程动态创建对象方法表的定位方法的方式,一般称之为动态绑定机制。

    最后说明,域和静态方法都是不具有多态性的,任何的域访问操作都将由编译器解析,因此不是多态的。静态方法是跟类,而并非单个对象相关联的。
    参考文章:http://hxraid.iteye.com/blog/428891

    展开全文
  • 【解惑】Java动态绑定机制的内幕

    千次阅读 2016-08-04 11:35:33
    Java方法调用的过程... 这篇文章我们就将揭露JVM方法调用的静态(static binding) 和动态绑定机制(auto binding) 。     ★ 静态绑定机制 Java代码  //被调用的类  package hr.test;  class Father{

    在Java方法调用的过程中,JVM是如何知道调用的是哪个类的方法源代码? 这里面到底有什么内幕呢? 这篇文章我们就将揭露JVM方法调用的静态(static binding) 动态绑定机制(auto binding) 

     

     

    ★ 静态绑定机制

    Java代码   收藏代码
    1. //被调用的类  
    2. package hr.test;  
    3. class Father{  
    4.       public static void f1(){  
    5.               System.out.println("Father— f1()");  
    6.       }  
    7. }  
    8. //调用静态方法  
    9. import hr.test.Father;  
    10. public class StaticCall{  
    11.        public static void main(){  
    12.             Father.f1(); //调用静态方法  
    13.        }  
    14. }  

         上面的源代码中执行方法调用的语句(Father.f1())被编译器编译成了一条指令:invokestatic #13。我们看看JVM是如何处理这条指令的

         (1) 指令中的#13指的是StaticCall类的常量池中第13个常量表的索引项(关于常量池详见《Class文件内容及常量池 》)。这个常量表(CONSTATN_Methodref_info ) 记录的是方法f1信息的符号引用(包括f1所在的类名,方法名和返回类型)。JVM会首先根据这个符号引用找到方法f1所在的类的全限定名: hr.test.Father。

         (2) 紧接着JVM会加载、链接和初始化Father类。

         (3) 然后在Father类所在的方法区中找到f1()方法的直接地址,并将这个直接地址记录到StaticCall类的常量池索引为13的常量表中。这个过程叫常量池解析 ,以后再次调用Father.f1()时,将直接找到f1方法的字节码。

         (4) 完成了StaticCall类常量池索引项13的常量表的解析之后,JVM就可以调用f1()方法,并开始解释执行f1()方法中的指令了。

     

         通过上面的过程,我们发现经过常量池解析之后,JVM就能够确定要调用的f1()方法具体在内存的什么位置上了。实际上,这个信息在编译阶段就已经在StaticCall类的常量池中记录了下来。这种在编译阶段就能够确定调用哪个方法的方式,我们叫做 静态绑定机制 

     

         除了被static 修饰的静态方法,所有被private 修饰的私有方法、被final 修饰的禁止子类覆盖的方法都会被编译成invokestatic指令。另外所有类的初始化方法<init>和<clinit>会被编译成invokespecial指令。JVM会采用静态绑定机制来顺利的调用这些方法。

     

     

     

    ★ 动态绑定机制

    Java代码   收藏代码
    1. package hr.test;  
    2. //被调用的父类  
    3. class Father{  
    4.     public void f1(){  
    5.         System.out.println("father-f1()");  
    6.     }  
    7.         public void f1(int i){  
    8.                 System.out.println("father-f1()  para-int "+i);  
    9.         }  
    10. }  
    11. //被调用的子类  
    12. class Son extends Father{  
    13.     public void f1(){ //覆盖父类的方法  
    14.         System.out.println("Son-f1()");  
    15.     }  
    16.         public void f1(char c){  
    17.                 System.out.println("Son-s1() para-char "+c);  
    18.         }  
    19. }  
    20.   
    21. //调用方法  
    22. import hr.test.*;  
    23. public class AutoCall{  
    24.     public static void main(String[] args){  
    25.         Father father=new Son(); //多态  
    26.         father.f1(); //打印结果: Son-f1()  
    27.     }  
    28. }  

          上面的源代码中有三个重要的概念:多态(polymorphism) 方法覆盖 、方法重载 。打印的结果大家也都比较清楚,但是JVM是如何知道f.f1()调用的是子类Sun中方法而不是Father中的方法呢?在解释这个问题之前,我们首先简单的讲下JVM管理的一个非常重要的数据结构——方法表 

     

           在JVM加载类的同时,会在方法区中为这个类存放很多信息(详见《Java 虚拟机体系结构 》)。其中就有一个数据结构叫方法表。它以数组的形式记录了当前类及其所有超类的可见方法字节码在内存中的直接地址 。下图是上面源代码中Father和Sun类在方法区中的方法表:

     

          上图中的方法表有两个特点:(1) 子类方法表中继承了父类的方法,比如Father extends Object。 (2) 相同的方法(相同的方法签名:方法名和参数列表)在所有类的方法表中的索引相同。比如Father方法表中的f1()和Son方法表中的f1()都位于各自方法表的第11项中。

     

          对于上面的源代码,编译器首先会把main方法编译成下面的字节码指令:

    多态调用的字节码指令代码   收藏代码
    1. 0  new hr.test.Son [13] //在堆中开辟一个Son对象的内存空间,并将对象引用压入操作数栈  
    2. 3  dup    
    3. 4  invokespecial #7 [15] // 调用初始化方法来初始化堆中的Son对象   
    4. 7  astore_1 //弹出操作数栈的Son对象引用压入局部变量1中  
    5. 8  aload_1 //取出局部变量1中的对象引用压入操作数栈  
    6. 9  invokevirtual #15 //调用f1()方法  
    7. 12  return  

           其中invokevirtual指令的详细调用过程是这样的:

           (1) invokevirtual指令中的#15指的是AutoCall类的常量池中第15个常量表的索引项。这个常量表(CONSTATN_Methodref_info ) 记录的是方法f1信息的符号引用(包括f1所在的类名,方法名和返回类型)。JVM会首先根据这个符号引用找到调用方法f1的类的全限定名: hr.test.Father。这是因为调用方法f1的类的对象father声明为Father类型。

           (2) 在Father类型的方法表中查找方法f1,如果找到,则将方法f1在方法表中的索引项11(如上图)记录到AutoCall类的常量池中第15个常量表中(常量池解析 )。这里有一点要注意:如果Father类型方法表中没有方法f1,那么即使Son类型中方法表有,编译的时候也通过不了。因为调用方法f1的类的对象father的声明为Father类型。

           (3) 在调用invokevirtual指令前有一个aload_1指令,它会将开始创建在堆中的Son对象的引用压入操作数栈。然后invokevirtual指令会根据这个Son对象的引用首先找到堆中的Son对象,然后进一步找到Son对象所属类型的方法表。过程如下图所示:

                        

          (4) 这是通过第(2)步中解析完成的#15常量表中的方法表的索引项11,可以定位到Son类型方法表中的方法f1(),然后通过直接地址找到该方法字节码所在的内存空间。

     

          很明显,根据对象(father)的声明类型(Father)还不能够确定调用方法f1的位置,必须根据father在堆中实际创建的对象类型Son来确定f1方法所在的位置。这种在程序运行过程中,通过动态创建的对象的方法表来定位方法的方式,我们叫做 动态绑定机制 

     

          上面的过程很清楚的反映出在方法覆盖的多态调用的情况下,JVM是如何定位到准确的方法的。但是下面的调用方法JVM是如何定位的呢?(仍然使用上面代码中的Father和Son类型)

    Java代码   收藏代码
    1. public class AutoCall{  
    2.        public static void main(String[] args){  
    3.              Father father=new Son();  
    4.              char c='a';  
    5.              father.f1(c); //打印结果:father-f1()  para-int 97  
    6.        }  
    7. }  

           问题是Fahter类型中并没有方法签名为f1(char)的方法呀。但打印结果显示JVM调用了Father类型中的f1(int)方法,并没有调用到Son类型中的f1(char)方法。

     

           根据上面详细阐述的调用过程,首先可以明确的是:JVM首先是根据对象father声明的类型Father来解析常量池的(也就是用Father方法表中的索引项来代替常量池中的符号引用)。如果Father中没有匹配到"合适" 的方法,就无法进行常量池解析,这在编译阶段就通过不了。

          那么什么叫"合适"的方法呢?当然,方法签名完全一样的方法自然是合适的。但是如果方法中的参数类型在声明的类型中并不能找到呢?比如上面的代码中调用father.f1(char),Father类型并没有f1(char)的方法签名。实际上,JVM会找到一种“凑合”的办法,就是通过 参数的自动转型 来找 到“合适”的 方法。比如char可以通过自动转型成int,那么Father类中就可以匹配到这个方法了 (关于Java的自动转型问题可以参见《【解惑】Java类型间的转型 》)。但是还有一个问题,如果通过自动转型发现可以“凑合”出两个方法的话怎么办?比如下面的代码:

    Java代码   收藏代码
    1. class Father{  
    2.     public void f1(Object o){  
    3.         System.out.println("Object");  
    4.     }  
    5.     public void f1(double[] d){  
    6.         System.out.println("double[]");  
    7.     }  
    8.       
    9. }  
    10. public class Demo{  
    11.     public static void main(String[] args) {  
    12.         new Father().f1(null); //打印结果: double[]  
    13.     }  
    14. }  

            null可以引用于任何的引用类型,那么JVM如何确定“合适”的方法呢。一个很重要的标准就是:如果一个方法可以接受传递给另一个方法的任何参数,那么第一个方法就相对不合适。比如上面的代码: 任何传递给f1(double[])方法的参数都可以传递给f1(Object)方法,而反之却不行,那么f1(double[])方法就更合适。因此JVM就会调用这个更合适的方法。

     

     

    ★ 总结

     

          (1) 所有私有方法、静态方法、构造器及初始化方法<clinit>都是采用静态绑定机制。在编译器阶段就已经指明了调用方法在常量池中的符号引用,JVM运行的时候只需要进行一次常量池解析即可。


          (2) 类对象方法的调用必须在运行过程中采用动态绑定机制。

               首先,根据对象的声明类型(对象引用的类型)找到“合适”的方法。具体步骤如下:

               ① 如果能在声明类型中匹配到方法签名完全一样(参数类型一致)的方法,那么这个方法是最合适的。

               ② 在第①条不能满足的情况下,寻找可以“凑合”的方法。标准就是通过将参数类型进行自动转型之后再进行匹配。如果匹配到多个自动转型后的方法签名f(A)和f(B),则用下面的标准来确定合适的方法:传递给f(A)方法的参数都可以传递给f(B),则f(A)最合适。反之f(B)最合适 。

     

               ③ 如果仍然在声明类型中找不到“合适”的方法,则编译阶段就无法通过。

     

               然后,根据在堆中创建对象的实际类型找到对应的方法表,从中确定具体的方法在内存中的位置。

     

     

     

    ★ 覆写(override)

           一个实例方法可以覆写(override)在其超类中可访问到的具有相同签名的所有实例方法,从而使能了动态分派(dynamic dispatch);换句话说,VM将基于实例的运行期类型来选择要调用的覆写方法。覆写是面向对象编程技术的基础,并且是唯一没有被普遍劝阻的名字重用形式:

    Java代码   收藏代码
    1. class Base{  
    2.       public void f(){}  
    3. }  
    4. class Derived extends Base{  
    5.       public void f(){}  
    6. }  

     

     

    ★ 隐藏(hide)

           一个域、静态方法或成员类型可以分别隐藏(hide)在其超类中可访问到的具有相同名字(对方法而言就是相同的方法签名)的所有域、静态方法或成员类型。隐藏一个成员将阻止其被继承。

    Java代码   收藏代码
    1. class Base{  
    2.       public static void f(){}  
    3. }  
    4. class Derived extends Base  {  
    5.       private static void f(){}   //hides Base. f()  
    6. }  
     

    ★ 重载(overload) 
          在某个类中的方法可以重载(overload)另一个方法,只要它们具有相同的名字和不同的签名。由调用所指定的重载方法是在编译期选定的。

    Java代码   收藏代码
    1. class CircuitBreaker{  
    2.       public void f (int i){}    //int overloading  
    3.       public void f(String s){}   //String overloading  
    4. }  
     

    ★ 遮蔽(shadow)
         一个变量、方法或类型可以分别遮蔽(shadow)在一个闭合的文本范围内的具有相同名字的所有变量、方法或类型。如果一个实体被遮蔽了,那么你用它的简单名是无法引用到它的;根据实体的不同,有时你根本就无法引用到它。

    Java代码   收藏代码
    1. class WhoKnows{  
    2.     static String sentence=”I don't know.”;  
    3.     public static void main(String[] args〕{  
    4.            String sentence=”I don't know.”;  //shadows static field  
    5.            System.out. println (sentence);  // prints local variable  
    6.     }  
    7. }  

          尽管遮蔽通常是被劝阻的,但是有一种通用的惯用法确实涉及遮蔽。构造器经常将来自其所在类的某个域名重用为一个参数,以传递这个命名域的值。这种惯用法并不是没有风险,但是大多数Java程序员都认为这种风格带来的实惠要超过
    其风险:

    Java代码   收藏代码
    1. class Belt{  
    2.       private find int size ;  //Parameter shadows Belt. size  
    3.       public Belt (int size){  
    4.            this. size=size;  
    5.       }  
    6. }  

     

    ★ 遮掩(obscure)

           一个变量可以遮掩具有相同名字的一个类型,只要它们都在同一个范围内:如果这个名字被用于变量与类型都被许可的范围,那么它将引用到变量上。相似地,一个变量或一个类型可以遮掩一个包。遮掩是唯一一种两个名字位于不同的名字空间的名字重用形式,这些名字空间包括:变量、包、方法或类型。如果一个类型或一个包被遮掩了,那么你不能通过其简单
    名引用到它,除非是在这样一个上下文环境中,即语法只允许在其名字空间中出现一种名字。遵守命名习惯就可以极大地消除产生遮掩的可能性:

    Java代码   收藏代码
    1. public class Obscure{  
    2.       static String System;// Obscures type java.lang.System  
    3.       public static void main(String[] args)  
    4.             // Next line won't compile:System refers to static field  
    5.             System. out. println(“hello, obscure world!”);  
    6.       }  
    7. }  


    附带一个基础面试题可以用来理解动态绑定机制 结合上面所说的 分析一下你自己了解动态绑定的程度

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    public class OverrideTest {
    	public void f(Collection c) {
    		System.out.println("Collection");
    	}
    
    	public void f(List m) {
    		System.out.println("List");
    	}
    
    	public void f(Set m) {
    		System.out.println("Set");
    	}
    
    	public static void main(String[] args) {
    		OverrideTest overrideTest = new OverrideTest();
    		Collection[] c = { new ArrayList(), new HashSet() };
    		for (int i = 0; i < c.length; i++) {
    			overrideTest.f(c[i]);
    		}
    	}
    }


    展开全文
  • java多态总结以及动态绑定机制

    千次阅读 2016-07-19 00:35:10
    1.继承体现了多态,JAVA里没有多继承,一个类之能有一个父类。而继承的表现就是多态。一个父类可以有多个子类,而在子类里可以重写父类的方法(例如方法print()),这样每个子类里重写的代码不一样,自然表现形式就...
    什么是多态?
    
    1.继承体现了多态,JAVA里没有多继承,一个类之能有一个父类。而继承的表现就是多态。一个父类可以有多个子类,而在子类里可以重写父类的方法(例如方法print()),这样每个子类里重写的代码不一样,自然表现形式就不一样。这样用父类的变量去引用不同的子类,在调用这个相同的方法print()的时候得到的结果和表现形式就不一样了,这就是多态,相同的消息(也就是调用相同的方法)会有不同的结果。
    例如A类被几个子类继承,子类都重写了A类中的某个方法M,调用(A类型引用对象a).M的时候就会根据创建a的时候使用的是具体哪个子类而调用相应子类中的方法M,这就体现了程序的多态性。
    在父类中声明的方法,可以在子类中进行覆盖(声明为finial的除外),这样,父类的引用在引用子类对象的时候可以做出不同的响应。
    所以继承中多态表现为:相同的消息被发送到子类或父类对象上,将导致完全不同的行为。多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。
    2.接口体现了多态,接口一般是功能的封装,正常一个接口可以有多个实现,根据不同的需求来编写实现类,jvm会自动将实现了接口的类和接口绑定起来,虽然接口本身没有实际意义,但是程序只需要调用接口即可调用实现该接口的特定类的方法,一个接口不同的实现类体现了多态。尽管接口的方法没有实际意义,需要具体的类直接实现接口才可使用,需要什么样的直接实现即可,可插拔式,系统自动将接口和实现类绑定。调用的是接口中声明的方法,而具体的功能是看你是用哪个实现来初始化这个对象的。
    JAVA没有多继承,而接口实现了多继承!一个类可以同时实现多个接口从而变相实现多继承,例如
    public class Son implements Father1,Father2,Father3{   
    }
    但是其缺点就是如果向一个java接口加入一个新的方法时,所有实现这个接口的类都得编写具体的实现。


    生活中的多态:
    关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。


    多态性的特征:
    发送消息给某个对象,让该对象自行决定响应何种行为。 通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。
    但是多态性未必一定要用接口,只要在继承的基础上存在方法的重写、重载与动态连接即可体现多态性(如存在继承关系的类之间)
    java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
    如果a是类A的一个引用,那么,a可以指向类A的一个实例,或者说指向类A的一个子类。
    如果a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。但是jvm会自动将接口引用和实现类绑定。


    多态存在的三个必要条件
    一、要有继承;
    二、要有重写;
    三、父类引用指向子类对象。



    实现多态的技术支持:动态绑定,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法,下面具体介绍。

    程序绑定的概念:
    绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定(编译是绑定)和后期绑定(运行是绑定)。
    也可以理解为该方法和所属于类是否绑定的关系。

    静态绑定:
    在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。例如:C。
    针对java简单的可以理解为程序编译期的绑定;这里特别说明一点,java当中的方法只有final,static,private和构造方法是前期绑定
     
    动态绑定:
    后期绑定:在运行时根据具体对象的类型进行绑定。
    若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。
    动态绑定的过程:
    虚拟机提取对象的实际类型的方法表;
    虚拟机搜索方法签名;
    调用方法。
     
    关于绑定相关的总结:
    在了解了三者的概念之后,很明显我们发现java属于后期绑定。在java中,几乎所有的方法都是后期绑定的,在运行时动态绑定方法属于子类还是基类。但是也有特殊,针对static方法和final方法由于不能被继承,因此在编译时就可以确定他们的值,他们是属于前期绑定的。特别说明的一点是,private声明的方法和成员变量不能被子类继承,也就是该方法只属于本类,所以方法和类绑定了,所有的private方法都被隐式的指定为final的(由此我们也可以知道:将方法声明为final类型的一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)。java中的后期绑定是有JVM来实现的,我们不用去显式的声明它,而C++则不同,必须明确的声明某个方法具备后期绑定。
     
    总结:对于java当中的方法而言,除了final,static,private和构造方法是前期绑定外,其他的方法全部为动态绑定。而动态绑定的典型发生在父类和子类的转换声明之下:jvm在编译类时候,发现如果方法是final,static,private修饰或者构造方法,则判断为静态绑定,是属于类的,它们只可以被该类调用,那么此时这些方法的方法本体可以确定。否则为动态绑定,也就是此时无法确定这些方法可以被谁调用,只有在方法被调用的语句出现时候,再去根据对象引用所指实际对象去确定调用特定类的方法。例如若该方法被父类对象调用,同时该方法在子类中被重写,那么最终就会调用子类覆盖的方法体。
    JAVA 虚拟机调用一个类方法时(静态方法),它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当虚拟机调用一个实例方法时,它会基于对象实际的类型(只能在运行时得知)来选择所调用的方法,这就是动态绑定,是多态的一种。当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同x所指向的对象的实际类型相匹配的方法版本。动态绑定为解决实际的业务问题提供了很大的灵活性,是一种非常优美的机制。

    注意:
    与方法不同,在处理java类中的成员变量(实例变量和类变量)时,并不是采用运行时绑定,而是一般意义上的静态绑定。所以在向上转型的情况下,对象的方法可以找到子类,而对象的属性(成员变量)还是父类的属性(子类对父类成员变量的隐藏)


    理解了“向上转型”和“类型还原”便于理解多态性,此处不再赘述。
    本人csdn转载链接:http://blog.csdn.net/yuliangliang092/article/details/51948283

    一个经典的多态题目:

    class A ...{  
             public String show(D obj)...{  
                    return ("A and D");  
             }   
             public String show(A obj)...{  
                    return ("A and A");  
             }   
    }   
    class B extends A...{  
             public String show(B obj)...{  
                    return ("B and B");  
             }  
             public String show(A obj)...{  
                    return ("B and A");  
             }   
    }  
    class C extends B...{}   
    class D extends B...{}  


    问题:以下输出结果是什么?
    A a1 = new A();  
            A a2 = new B();  
            B b = new B();  
            C c = new C();   
            D d = new D();   
            System.out.println(a1.show(b));   ①  
            System.out.println(a1.show(c));   ②  
            System.out.println(a1.show(d));   ③  
            System.out.println(a2.show(b));   ④  
            System.out.println(a2.show(c));   ⑤  
            System.out.println(a2.show(d));   ⑥  
            System.out.println(b.show(b));    ⑦  
            System.out.println(b.show(c));    ⑧  
            System.out.println(b.show(d));    ⑨     


     答案


    ①   A and A
    ②   A and A
    ③   A and D
    ④   B and A
    ⑤   B and A
    ⑥   A and D
    ⑦   B and B
    ⑧   B and B
    ⑨   A and D

    解析:
    1.实例对象为A,参数为对象B,B为A的子类。执行A.class中show(A obj)
    2.同上
    3.实例对象为A,参数为对象D,执行A.class中show(D obj)
    4.实例对象依然为A,参数为B,本应执行A.class中show(A obj),但是,B.class重写了show(A obj),所以执行B.class show(A obj)
    5.同上
    6.执行A.class show(D obj) B中并没有重写。
    7,8.实例对象为B,参数为B或者B的子类,执行show(B obj)
    9.实例对象为B,参数为D,因为B继承自A,也可以执行A中的show(D obj)



    展开全文
  • 动态绑定为解决实际的业务问题提供了很大的灵活性,是一种非常优美的机制。  1. Java对象模型  Java虚拟机规范并没有规定Java对象在堆里是如何表示的。对象的内部表示也影响着整个堆以及垃圾收集器的设计,它由...
  • 详解Java动态绑定和静态绑定机制

    千次阅读 2019-04-22 09:27:42
    https://jiayouxujin.github.io/2019/04/20/java动态绑定和静态绑定详解/ 这一次,彻底搞懂动态绑定和静态绑定机制
  • Java动态绑定机制(非常重要) Java重要特性:动态绑定机制 运行时绑定也叫动态绑定,它是一种调用对象方法的机制。Java调用对象方法时,一般采用运行时绑定机制。 1.Java的方法调用过程 编译器查看对象的声明...
  • JAVA动态绑定的内部实现机制

    万次阅读 多人点赞 2010-05-08 13:32:00
    JAVA动态绑定的内部实现机制 JAVA虚拟机调用一个类方法时,它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当虚拟机调用一个实例方法时,它会基于对象实际的类型(只能在运行时得知)来选择所...
  • Java动态绑定和静态绑定

    千次阅读 多人点赞 2017-09-14 17:05:16
    Java来说,绑定分为静态绑定动态绑定;或者叫做前期绑定和后期绑定。静态绑定在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。例如:C。针对Java,可以简单地理解为程序编译期的绑定。这里特别...
  • Java静态绑定动态绑定的理解

    千次阅读 2018-01-16 12:48:34
    前言本人在学习虚拟机类加载机制时看到,java中类型的加载、连接和初始化过程都是在程序运行时期完成的。这种策略虽然会令类加载时稍微增加一些性能开销,但是会为java应用程序提供高度的灵活性...java绑定程序绑定的概
  • Java动态绑定机制

    2015-03-12 18:04:25
    Java动态绑定又称为运行时绑定。意思就是说,程序会在运行的时候自动选择调用哪儿个方法。 一、动态绑定的过程:  例子: 1 public class Son extends Father2 Son son = new Son();3 son.method(); ...
  • java来说,绑定分为前期绑定(静态绑定)与后期绑定动态绑定)。静态绑定: 在程序执行前方法已经被绑定(也就是说在编译过程中就已经知道这个方法到底是哪个类中的方法),此时由编译器或其它连接程序实现。 ...
  • Java的静态/动态绑定

    2020-12-22 15:20:53
    看到《Java核心技术I》书上的动态绑定,意思是当子类和父类存在同一个方法,子类重写了父类的方法,程序在运行时调用方法是调用父类的方法还是子类的重写方法呢?程序会在运行的时候自动选择调用某个方法(根据方法...
  • Java静态绑定动态绑定

    万次阅读 多人点赞 2014-04-19 00:31:12
    java来说,绑定分为静态绑定动态绑定;或者叫做前期绑定和后期绑定. 静态绑定: 在程序执行前方法已经被绑定(也就是说在编译过程中就已经知道这个方法到底是哪个类中的方法),此时由编译器或其它连接程序...
  • 日历表格面板 [ConfigLine.java] 控制条类 [RoundBox.java] 限定选择控件 [MonthMaker.java] 月份表算法类 [Pallet.java] 调色板,统一配色类 Java扫雷源码 Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接...
  • java 动态绑定与c++动态绑定区别

    千次阅读 2013-03-14 23:03:15
    从c++到java(四) 静态绑定动态绑定(上)  对比下面,在同样不使用虑函数的情况下,指向导出类的基类指针在调用导出类中重载了的方法时结果不同。 C++代码: class A{  public:  void ...
  • JVM机制、静态绑定动态绑定的一些个人理解。
  • Java中的多态和动态绑定

    千次阅读 2016-05-31 18:26:57
    动态绑定的内部机制  1. 定义根据Core Java: 多态:一个对象变量可以指示多种实例类型的现象。 动态绑定:在运行时刻能够自动选择调用哪个方法的现象。 签名:方法名和参数列表构成一个签名 多态和动态绑定...
  • 我查了一些手头仅有的资料(包括书籍,QQ群,百度),找到的答案都不是很明确,自己目前的理解如下, ...另外,马士兵在他的java基础视频教学中说“多态就是动态绑定”.....感觉不太对阿.. 求大虾指点~~
  • 一个Java程序的执行要经过编译和执行(解释)这两个步骤,...这里首先我们将确定这种调用何种方法实现或者变量的操作叫做绑定。(执行父类方法还是子类方法是由对象决定的,跟引用没有直接关系)  一:在Java中存
  • Java基础知识面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 12:11:27
    文章目录Java概述何为编程什么是Javajdk1.5之后的三大版本JVM、JRE和JDK的关系什么是跨平台性?原理是什么Java语言有哪些特点什么是字节码?采用字节码的最大好处是什么什么是Java程序的主类?应用程序和小程序的...
  • Java动态绑定是什么意思?

    千次阅读 2019-01-06 16:33:23
    如果绑定过程在程序运行期间进行,以对象的类型为基础,则称为“后期绑定”或“动态绑定”。 如果一种语言实现了后期绑定,同时必须提供一些机制,可以在运行期间判断对象的实际类型,并分别调用适当的方法,即...
  • Java多态机制的实现依赖于其动态绑定。 (本文默认读者已经了解Java程序的编译和运行时问题。)1、程序绑定的概念 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。  对Java来说,绑定分为静态绑定...
  • java动态绑定内部实现

    2014-09-29 12:09:31
    JAVA动态绑定的内部实现机制  JAVA虚拟机调用一个类方法时,它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当虚拟机调用一个实例方法时,它会基于对象实际的类型(只能在运行时得知)来选择...
  • [日期:2016-12-20] 来源: 作者: [字体:大 中 小] ... 这篇文章我们就将揭露JVM方法调用的静态(static binding) 和动态绑定机制(auto binding) 。  静态绑定机制  //被调用的类  package hr.test;
  • 动态绑定实现机制

    千次阅读 2016-05-10 11:46:44
    来源:伯乐在线专栏作者-陶邦仁 Java虚拟机调用一个类方法时,它会基于对象引用的...动态绑定为解决实际的业务问题提供了很大的灵活性,是一种非常优美的机制。 1. Java对象模型 Java虚拟机规范并没有规定J
  • java动态绑定和静态绑定

    千次阅读 2018-07-23 10:58:10
    背景 1.当子类和父类存在同一个方法,子类重写了父类的方法,程序...在java中存在绑定机制解决以上疑问。   绑定 绑定:将一个方法的调用与方法所在的类(方法主体)关联起来。即决定调用哪个方法和变量。 ...
  • java 动态绑定 以及 内部实现机制

    千次阅读 2012-04-28 12:25:38
    java来说,绑定分为静态绑定动态绑定;或者叫做前期绑定和后期绑定。  (1)静态绑定:  在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。例如:C。  针对java简单的可以理解为程序编译...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 186,395
精华内容 74,558
关键字:

java动态绑定机制

java 订阅