• Java JVM 多态(动态绑定)


    Java JVM 多态(动态绑定)

    @author ixenos

     摘要:绑定、动态绑定实现多态、多态的缺陷、纯继承与扩展接口、向下转型与RTTI

    绑定


    将一个方法的调用和一个方法的主体关联起来,称作(方法调用)绑定:

      1.前期绑定:在程序执行前绑定(由编译器和连接程序实现);

      2.后期绑定:在运行时根据对象的类型绑定(也称动态绑定运行时绑定);

              a) 实现条件:能在运行时判断对象的类型,从而关联对应主体,调用其方法

              b) 编译器一直不知道真实对象类型,只将其认作引用变量的类型且知道有继承关系

              c) Java中除了static方法final方法private方法相当于final方法)是前期绑定之外,其他所有的方法都是后期绑定

    动态绑定实现多态


    多态作用:

      1.消除类型之间的耦合关系,使我们可以编写只与基类打交道的程序代码了(比如List<String> = new ArrayList<>();)

      2.使程序有可扩展性,我们可从通用的基类继承出新的数据类型从而添加新的功能,而不需要改变对应接受参数的方法,只与基类接口通信

     

    动态绑定:

      1. 假设B extends A,若使A = new B()向上转型,则编译器认为这是合理的协变,编译通过!但此时编译器只知道B是A的子类,无法得知B的具体类型!

      2. 运行时,Java的后期绑定机制判定该对象new B()的运行时类型为B,所以方法的调用策略是从B类中调用相应方法(static、final方法除外)

      3. 动态绑定(后期绑定或运行时绑定)常被指称为多态

    多态的缺陷


     “覆盖”私有方法时

     1 public class Pri{
     2     private void f(){ System.out.println("I'm private f()"); }
     3 
     4     public static void main(String[] args){
     5         Pri pri = new Pub(); //向上转型,动态调用Pub类方法
     6         pri.f();
     7     }
     8 }
     9 
    10 class Pub{
    11     public void f(){ System.out.println("I'm public f()"); }
    12 }
    13 
    14 
    15 ----------------------------------
    16 输出:  I'm private f()
    View Code

      我们期望输出是public f(),但private方法被认为是final方法,属于前期绑定,这对导出类(子类)是屏蔽的,因此没有重写!只是重载了但我们不知道

      此时,Pub类中的f()方法就是一个全新的方法,两个方法存在不同类中

      结论:虽然只有非private方法可以覆盖,但也要注意这种试图覆盖private方法的行为,毕竟编译器不会报错(因为根本就是创建一个新的方法)导致方法不按照初衷来执行

    不适用多态的范围:域、静态方法与final方法

      只有普通方法的调用可以是多态的

      :当Sub对象转型为Super引用时,任何域访问操作都由编译器解析,因此不是多态的;

        而此时将为Super.field和Sub.field分配不同存储空间,此时Sub包含两个域,他自己的和从super得到的;

        若Super.field是private的,那么Sub是看不到的;

        若Super.field是public且子类域覆盖了父类域,Sub的默认域就是子类域,要调用父类域要用super.field(field替换为父类域引用变量)

      静态方法:静态方法是与类相关联的而不是与对象

      final方法:final阻止子类重写,但可重载

    纯继承与扩展接口


    纯继承:

    导出类只覆盖基类的方法,导出类只具有和基类相同的接口,此时导出类可以完全代替基类,基类可以接受发送给导出类的任何信息,我们只需要从导出类向上转型,永远不需要知道正在处理的对象的确切类型,这是通过多态(动态绑定)处理的

     1 Shape{
     2     draw(){}
     3     erase(){}
     4 }
     5 
     6 Circle extends Shape{
     7     draw(){}
     8     erase(){}
     9 }
    10 
    11 Square extends Shape{
    12     draw(){}
    13     erase(){}
    14 }
    15 
    16 Triangle extends Shape{
    17     draw(){}
    18     erase(){}
    19 }
    View Code

    扩展接口:

    扩展导出类的功能,但是,导出类中接口的扩展部分不能被基类访问,因此,一旦扩展接口的导出类对象向上转型,就不能调用那些新方法

     1 Useful{
     2     draw(){}
     3     erase(){}
     4 }
     5 
     6 MoreUseful extends Useful{
     7     draw(){}
     8     erase(){}
     9 
    10     //扩展接口
    11     freak(){} 
    12     flyme(){}
    13 }
    View Code

    向下转型与RTTI


    向上转型:

    向上转型是安全的,因为基类不会具有大于导出类的接口

    向下转型:

    向下转型是不安全的,由“运行时类型识别”RTTI(Run-Time Type Identification)来确保向下转型正确性

     1 Useful{
     2      draw(){}
     3      erase(){}
     4  }
     5  
     6 MoreUseful extends Useful{
     7      draw(){}
     8      erase(){}
     9  
    10      //扩展接口
    11      freak(){} 
    12      flyme(){}
    13  }
    14 
    15 public class RTTI{
    16     public static void mian(String[] args){
    17         Useful[] x = { new Useful() , new MoreUseful() };
    18         
    19        // ((MoreUseful)x[0]).freak(); //ERROR 向下转型失败,因为x[0]对象不具有MoreUseful类中的扩展接口
    20 
    21         ((MoreUseful)x[1]).freak();  //向下转型成功/RTTI   
    22 
    23 
    24 
    25 
    26 
    27 
    28 
    29   
    RTTI行为示例

     一个基类引用的对象,如果想访问特定导出类对象的扩展接口,就可以尝试向下转型,但是该基类对象本身必须是导出类类型或者其子类型,才能转型成功

      

  • 相关阅读:
    优先队列
    Problem W UVA 662 二十三 Fast Food
    UVA 607 二十二 Scheduling Lectures
    UVA 590 二十一 Always on the run
    UVA 442 二十 Matrix Chain Multiplication
    UVA 437 十九 The Tower of Babylon
    UVA 10254 十八 The Priest Mathematician
    UVA 10453 十七 Make Palindrome
    UVA 10163 十六 Storage Keepers
    UVA 1252 十五 Twenty Questions
  • 原文地址:https://www.cnblogs.com/ixenos/p/5677295.html
Copyright © 2020-2023  润新知