博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java多态 动态绑定_Java JVM 多态(动态绑定)
阅读量:6279 次
发布时间:2019-06-22

本文共 2612 字,大约阅读时间需要 8 分钟。

Java JVM 多态(动态绑定)

@author ixenos

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

绑定

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

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

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

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

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

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

动态绑定实现多态

多态作用:

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

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

动态绑定:

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

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

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

多态的缺陷

“覆盖”私有方法时:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 public classPri{2 private void f(){ System.out.println("I'm private f()"); }3

4 public static voidmain(String[] args){5 Pri pri = new Pub(); //向上转型,动态调用Pub类方法

6 pri.f();7 }8 }9

10 classPub{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阻止子类重写,但可重载

纯继承与扩展接口

纯继承:

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 Shape{2 draw(){}3 erase(){}4 }5

6 Circle extendsShape{7 draw(){}8 erase(){}9 }10

11 Square extendsShape{12 draw(){}13 erase(){}14 }15

16 Triangle extendsShape{17 draw(){}18 erase(){}19 }

View Code

扩展接口:

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 Useful{2 draw(){}3 erase(){}4 }5

6 MoreUseful extendsUseful{7 draw(){}8 erase(){}9

10 //扩展接口

11 freak(){}12 flyme(){}13 }

View Code

向下转型与RTTI

向上转型:

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

向下转型:

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 Useful{2 draw(){}3 erase(){}4 }5

6 MoreUseful extendsUseful{7 draw(){}8 erase(){}9

10 //扩展接口

11 freak(){}12 flyme(){}13 }14

15 public classRTTI{16 public static voidmian(String[] args){17 Useful[] x = { new Useful() , newMoreUseful() };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行为示例

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

转载地址:http://uznva.baihongyu.com/

你可能感兴趣的文章
运维基础命令
查看>>
入门到进阶React
查看>>
SVN 命令笔记
查看>>
检验手机号码
查看>>
重叠(Overlapped)IO模型
查看>>
Git使用教程
查看>>
使用shell脚本自动监控后台进程,并能自动重启
查看>>
Flex&Bison手册
查看>>
solrCloud+tomcat+zookeeper集群配置
查看>>
/etc/fstab,/etc/mtab,和 /proc/mounts
查看>>
Apache kafka 简介
查看>>
socket通信Demo
查看>>
技术人员的焦虑
查看>>
js 判断整数
查看>>
建设网站应该考虑哪些因素
查看>>
mongodb $exists
查看>>
js实现页面跳转的几种方式
查看>>
sbt笔记一 hello-sbt
查看>>
常用链接
查看>>
pitfall override private method
查看>>