多态:程序绑定(动态绑定与静态绑定)

多态是一个运行时的行为,不是编译时行为。

程序绑定的概念:

绑定指的是一个方法的调用与方法所在的类或对象(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定。

静态绑定(前期绑定):

在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。发生在编译阶段,绑定的是类信息,即为定义的类的类型
针对java简单的可以理解为程序编译期的绑定;这里特别说明一点,java当中的方法,只有final,static,private,重载方法(overloaded methods)和构造方法是静态绑定
所有的变量都是静态绑定。

动态绑定(后期绑定):

在运行时根据具体对象的类型进行绑定。发生在运行阶段,绑定的是对象信息。
若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。
重写方法(overridden methods)使用的是动态绑定

动态绑定的过程:

  • 虚拟机提取对象实际类型的方法表;
  • 虚拟机搜索方法签名;
  • 调用方法。
  1. 编译器检查对象的声明类型和方法名。
    假设我们调用p.method()方法,并且p已经被声明为Child类型,那么编译器会列举出Child类中所有的名称为method的方法和从Child类的父类继承过来的method方法,如果发现子类重写了父类的方法,则父类方法被覆盖。
  2. 接下来编译器检查方法调用中提供的参数类型。如果在所有名称为method的方法中有一个参数类型和调用提供的参数类型最为匹配,那么就调用这个方法,这个过程叫做“重载解析”
    重载解析会匹配最精确的一个,一个方法或构造器可以接受传递给另一个方法或构造器的任何参数,那么我们就说第一个比第二个方法缺乏精确性[JLS 15.12.2.5]。
  3. 当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同p所指向的对象的实际类型相匹配的方法版本。假设child类定义了mehod()那么该方法被调用,否则就在child的父类(Parent类)中。
  4. 动态绑定时,参数会自动进行向上类型,但不会进行向下类型转换。

补充

这里容易混淆的点是静态绑定和动态绑定的区分,以及到底我们写的代码会调用什么方法,这里写了一个 Test 以供分析:
DynamicBingingTest.java

特别说明的一点是,private声明的方法和成员变量不能被子类继承,所有的private方法都被隐式的指定为final的,所以是静态绑定的。(由此我们也可以知道:将方法声明为final类型的一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)。

Java泛型会被抹除,但是编译阶段已经确保了泛型类型正确(但是 newInstance()方式可以Bug般地绕过泛型检查)。

你可能感兴趣的:(多态:程序绑定(动态绑定与静态绑定))