java多态的实现

      多态是面向对象技术的精华,可实现抽象的概括的去编码,运行时自动去匹配的效果,这符合我们人类的思考习惯。

      使用多态可有效降低当需求变动时对类必须修改的可能性。在java中绝大多数方法的调用都是多态的,也就是说java在编译期间并不能够确定应该调用哪个方法而是在运行时根据对象的信息自动查询到要调用的方法。


 其中实现的原理可看下面:

 * class A{
 *       public void f(){...}
 * }
 * class B extends A{
 * }
 * class C extends B{
 * }
 * A a = new C();
      这里创建了一个A类型的指针变量a,这里a指针变量的类型不是C类型而是A类,它所指向的对象却是C类型的,也就是说指针的类型和它指向的对象的类型并不是完全一致的,这个现象在java中经常出现,称之为指针泛化。
       执行a.f();

       那么java会到哪里去寻找一个f方法来给我们调用呢?

     应该执行哪一个方法的代码呢?

     有人说a指针的类型是A类型的就应该去调用A类方法。在目前以上代码这样说是没错的。



但我们修改一下代码。
 * class A{
 *       public void f(){...}
 * }
 * class B extends A{
 *       public void f(){...}
 * }
 * class C extends B{
 * }
    在B类中我们写了一个一模一样的f方法,这就是所谓的方法覆盖override。
目前我们有两个f方法,它们表面的形式是一样的。这次a.f()到底将调用哪个方法? java怎样会知道调用哪一个方法呢?
      事实上java是确实不知道的,java在编译阶段根本就不能确定我们要调用哪个方法,而是在运行的时候java根据对象中提供的信息去动态的查找到某一个方法。


那么java是如何去找到这种动态的信息的呢? 
对于内存中的每一个对象总是要有这样一个字段包含这样一个信息,它含有一个指针该指针指向了该对象的类型信息。
java多态的实现
A a = new C();
 例如这里这个对象是C类型的对象,虽然指针的类型是A,但这里对象的类型是C类型,那么这C类型的对象就一定包含了一个指针,这个指针指向了另一个对象,这个对象在java中就是一个特殊的称为类型信息一种数据。
      每一个类都会对应唯一的一个类型信息对象,我们这里有三个类那么在内存中至少有三个类型信息对象,实际情况当然要多得多比如隐含的Object或一些辅助类都会在内存中创建了一份类型信息对象。
      无论一个类创建了多少实例或者没有创建任何实例它在内存中的类型信息对象都永远只有一个, 该类型信息对象中保存了关于这个类的许多有用信息如类名字、实例大小、包含的方法定义等等。


看下面这个图:
java多态的实现
      new C()创建了C对象,在这个C类型对象中存在一个类型信息指针,这个指针指向C类的类型信息对象,在C类型信息对象中也有一个类似的叫类型信息指向,我们不妨称它为父类型信息指针,因为它总是指向该类的父类的类型信息对象,在这里当然是指向了C类的父类也就是B类型的信息对象,那么B类型的信息对象中也同样存在这样一个指针指向了B类型的父类也就是A类型的信息对象,再接下来就是Object类,因为Object没父类那么它的父类型信息指针就被置为了空指针,通过以上关联形成了一个链条。

      那当我们调用 a.f()时,首先会根据a指针找到C类对象,根据这个对象我们去找到了它的类型信息。(所以也就说在内存中我们虽然可以采用泛化方法把某一个对象当成另一种类型的对象来对待,但事实上当我们拿到指针后找到了对象仍然可以判定当初创建这个对象的类型是哪一个类因为它有这样一个类型信息指针)。


      这里我们找到了C类的类型信息,我们去询问是否含有f方法,假如含有我们就调用 ,显然C类型中并没有f方法,这样我们就沿着C类型信息的父类型信息指针找到了它的父类也就是B类型信息,再次询问同一个问题有没有f方法,这次回答是肯定的,我们找到了B类中的f方法,这样就直接去调用B类的f方法。这种查找方式总是能找到f方法的,因为我们在编译的时候保证了A类中至少会有这样的f方法,假如A类中没有f方法编译就无法通过了。


      以上就是多态的具体执行过程,它会在程序运行的过程中动态的去搜索匹配找到一个合适我们去调用的方法。