一、静态绑定和动态绑定的区别
在Java中,当你调用一个方法时,可能会在编译时期(compile time)解析(resolve),也可能实在运行时期(runtime) 解析,这全取决于到底是一个静态方法(static method )还是一个虚方法(virtual method)。如果是在编译时期解析,那么就称之为静态绑定(staticbinding)[换句话说静态方法就是在编译期解析的],如果方法的调用是在运行时期解析,那就是动态绑定(dynamic binding)或者延迟绑定(late binding)。
Java是一门面向对象的编程语言,优势就在于支持多态( Polymorphism)。多态使得父类型的引用变量可以引用子类型的对象。如果调用子类型对象的一个虛方法(非private,final or static),编译器将无法在子类中找到真正需要调用的方法,因为它可能是定义在父类型中的方法(从父类继承过来) ,也可能是在子类型中被重写(override) 的方法,这种情形,只能在运行时进行解析(说白了就是不能在编译期间确定要调用的方法在哪儿),因为只有在运行时期,才能明确具体的对象.到底是什么。这也是我们俗称的运行时或动态绑定( runtime or dynamic binding)。
另一方面,privatestatic和final方法将在编译时解析,因为编译器知道它们不能被重写,所有可能的方法都被定义在了一个类中,这些方法只能通过此类的引用变量进行调用。这叫做静态绑定或编译时绑定(static or compile time binding)。动态绑定只有在重写可能存在时才会用到,而重载的方法在编译时期即可确定( 因为重载的方法是在一个类中出现的)
总而言之,其区别如下:
①静态绑定在编译时期,动态绑定在运行时期。
②静态绑定只用到类型信息,方法的解析根据引用变量的类型决定,而动态绑定则根据实际引用的的对象决定(对象只有在程序运行的时候才会在内存中出现)
③在java中,private static和final 方法都是静态绑定,只有虚方法才是动态绑定
④多态是通过动态绑定实现的,我们上节课讲了多态的概念,那么今天你要知道之所以能实现多态就是Java提供了动态绑定机制。
二、动态绑定是如何实现的?
一个对象的多态方法的地址将被存储在该对象的方法表(methodtable)里面(每个对象都会有一个方法表)。在运行时期,调用多态方法的时候,JVM会在此表中搜索方法的名字,从而获取方法的地址。方法表里包含方法的名字和对应的地址(注意,这个地址是动态绑定的)。这个方法表对所有属于这个类的对象而言,都是一样的,所以它会存储在Class ( 我们会在反射的时候讲解)对象中(这里对象类型以Integer为例) ( 在其他的语言中,这样的表又叫做vtables,虚函数表)。需要说明的是,java语言中,如果没有添加任何关键字,则方法默认就是虚方法,任何子类都可以重写它。
方法表并不属于语言的一-部分,但是会有很多种不同的实现(不同的JVM提供商可以自由选择实现的细节,只要结果保证一致就ok) 。其中,Sun公司的JVM实现,则选择了将方法表入口放在对象的常量池( constant pool)里(这里的对象池是class文件中的一个区域)
下面,将通过一个图表实例来展示,对于某些类(这里以Integer为例)而言,是如何一步步构建方法表的。初始时,表都是空的。运行时,方法表将从最远的祖先类开始,逐步加入这些多态方法。通常,这个最远的祖先是Object类。
接下来,这个表中将加入第二远的祖先类的多态方法,如果已经存在,就修改其地址值(这就是子类对父类方法的重写原因)。此例中,第二远的类是Number类。如果你查看了javadoc,你就会发现Number类并没有重写任何方法,只是额外多了六个方法,因而,将这六个多的方法加入表中。此时,toString 项并没有被改变,方法表如下:
这个过程一-直持续下去,直到所有的父类的多态方法都被合并进这个表里。最后,方法表会被Integer类的多态方法所更新,此时,toString 方法会被重写:
方法表中的方法名这一项,只包含最初始的类名,所谓重写,只是修改了地址栏下的值,不会改变方法名的值。需要说明的是,此处,假如有Number num = new Integer(10),即便方法表里面有了Integer. parseInt方法,我们仍不能通过num来调用parseInt。也就是说,num变量引用了Integer对象,并且与上述方法表关联,但在编译时期时,编译器会根据语法规则,实行访问控制,num不能调用和访问独属于Integer的类方法,只能访问自己拥有访问权限的类方法,而这些方法中的某些方法,在运行过程中,名字未变,映射地址却发生了变化,因而调用的是所引用的子类Integer的实现。这点要弄清楚!
父类类型的引用不能调用子类新扩充的方法!
有完整的Java初级,高级对应的学习路线和资料!专注于java开发。分享java基础、原理性知识、JavaWeb实战、spring全家桶、设计模式、分布式及面试资料、开源项目,助力开发者成长!
欢迎关注微信公众号:码邦主