📜  Java中的动态方法Dispatch和运行时多态

📅  最后修改于: 2020-03-19 05:24:48             🧑  作者: Mango

先决条件:继承
方法覆盖是Java支持运行时多态性的方法之一。动态方法Dispatch是一种在运行时而不是编译时解决对覆盖方法的调用机制。

  • 当通过超类引用调用重写的方法时,Java根据调用发生时所引用的对象的类型,确定要执行该方法的哪个版本(超类/子类)。因此,该确定是在运行时进行的。
  • 在运行时,取决于所引用对象的类型(而不是引用变量的类型)来确定将执行哪个版本的重写方法
  • 超类引用变量可以引用子类对象。这也称为向上转换。Java使用此事实在运行时解析对重写方法的调用。


因此,如果超类包含被子类覆盖的方法,则当通过超类引用变量引用不同类型的对象时,将执行该方法的不同版本。这是说明动态方法分派的示例:

// Java程序,利用继承使用动态方法
class A
{
    void m1()
    {
        System.out.println("在A的m1方法内");
    }
}
class B extends A
{
    // 覆盖m1()
    void m1()
    {
        System.out.println("在B的m1方法内");
    }
}
class C extends A
{
    // 覆盖m1()
    void m1()
    {
        System.out.println("在C的m1方法内");
    }
}
// 测试代码
class Dispatch
{
    public static void main(String args[])
    {
        // A的实例
        A a = new A();
        // B的实例
        B b = new B();
        // C的实例
        C c = new C();
        // A的引用
        A ref;
        
        ref = a;
        // 调用A的m1()
        ref.m1();
        // b分配给ref
        ref = b;
        // 调用B的m1()
        ref.m1();
        // C分配给ref
        ref = c;
        // 代用C的m1()
        ref.m1();
    }
}

输出:

在A的m1方法内
在B的m1方法内
在C的m1方法内

说明:
上面的程序创建了一个名为A的超类,它有B和C的两个子类。这些子类覆盖了m1()方法。

    1. 在Dispatch类的main()方法内部,最初声明了A,B和C类型的对象。
      A a = new A(); // A类型实例
      B b = new B(); // B类型实例
      C c = new C(); // C类型实例

    2. 现在,还声明了一种类型为A的引用ref,该引用最初将指向null。
      A ref; // A类型的引用

    3. 现在,我们将对每种对象类型(A或B或C)的引用分配给ref,一个一个地引用,并使用该引用来调用m1()。如输出所示,执行的m1()的版本由调用时引用的对象的类型确定
      ref = a; // ref指向一个A对象
      ref.m1(); // 调用A的m1()版本

      ref = b; // ref指向一个b对象
      ref.m1(); // 调用B的m1()版本

      ref = c; // ref指向一个c对象
      ref.m1(); // 调用C的m1()版本

      数据成员的运行时多态

      在Java中,我们只能覆盖方法,而不能覆盖变量(数据成员),因此数据成员无法实现运行时多态。例如 :

      // Java展示成员变量无法完成运行时多态
      // class A
      class A
      {
          int x = 10;
      }
      // class B
      class B extends A
      {
          int x = 20;
      }
      // 测试代码
      public class Test
      {
          public static void main(String args[])
          {
              A a = new B(); // B的实例
              // A的成员变量将被获取
              System.out.println(a.x);
          }
      }

      输出:

      10

      说明:在上面的程序中,类A(超类)和B(子类)都具有公共变量’x’。现在,我们将类B的对象称为“ a”,它是类A的类型。由于未覆盖成员变量,因此语句“ a.x”将始终引用超类的数据成员。

 

动态方法分派的优势

  1. 动态方法分派允许Java支持 方法的覆盖这对于运行时多态性至关重要。
  2. 它允许类指定对其所有派生类都通用的方法,同时允许子类定义其中某些或所有方法的特定实现。
  3. 它还允许子类添加其特定的方法子类,以定义某些特定的实现。

静态与动态绑定

  • 静态绑定在编译时完成,而动态绑定在运行时完成。
  • 私有private,最终final和静态static方法和变量使用静态绑定并由编译器绑定,而覆盖的方法根据运行时对象的类型在运行时绑定。