java中的静态绑定与动态绑定

news/2024/11/13 22:54:17/文章来源:https://www.cnblogs.com/waczj/p/18542789

个人学习记录,欢迎大家指导

什么是多态?

一个引用变量,它可以引用任何子类的对象,这个引用变量是多态的。

绑定

将一个方法调用与对应方法主体关联起来被称为绑定。(也就是,执行一条方法调用语句时所对应执行的方法体,叫做该方法体和这条方法调用语句绑定了)

动态绑定

来看一段代码

public class Parent {public void m(){System.out.println("父类方法执行");}public static void main(String[] arg){List<Parent> list = Arrays.asList(new A(),new B(),new C());for(Parent p : list){p.m();}}
}class A extends Parent{public void m(){System.out.println("A类方法执行");}
}class B extends Parent{public void m(){System.out.println("B类方法执行");}
}class C extends Parent{public void m(){System.out.println("C类方法执行");}
}output:
A类方法执行
B类方法执行
C类方法执行

何为动态绑定?
上述代码中,第一次循环,p变量接受一个A对象,p.m()语句调用A的m()方法,第二次循环时,p变量接受一个B对象,p.m()语句调用B的m()方法,第三次循环时,p变量接受一个C对象,p.m()语句调用C的m()方法,3次循环都是执行的p.m()语句,每次循环p.m()语句绑定的方法体不一样,这种运行时根据变量p实际引用对象动态绑定方法体的方式,叫做动态绑定。动态绑定是为了执行子类重写的方法。

静态绑定

对于static、final、private修饰的方法,以及构造方法,这些方法不可被子类重写,它们采用静态绑定。

方法调用的具体过程

看一段代码

public class Fu {public void m(Object obj){System.out.println("父类Object方法执行");}public static void main(String[] args){Fu f = new Zi();f.m(1);}}class Zi extends Fu{public void m(Object obj){System.out.println("子类Object方法执行");}public void m(Integer i){System.out.println("子类Integer方法执行");}
}output:
子类Object方法执行

为啥选择子类的m(Object)方法执行,而不是m(Integer)方法执行呢,你肯定想说因为向上转型,不能执行子类特有的方法,确实是这样,但为什么呢?

  • 来看一下方法调用的过程
    编译期:
    1、编译器根据变量f声明的类型Fu,去Fu类中查找名字为m的方法。
    2、此时可能找到多个名为m的方法,因为方法是可以重载的,编译器再根据调用方法时提供的参数类型,去找到最匹配的方法,在这里是m(Object)方法,
    这个过程叫做重载解析。重载解析后,编译器获得了方法的签名(方法名+形参类型)。
    3、如果方法是被static、final、private修饰的,该方法不能被重写,f.m(1)运行时,无论f引用什么子类对象,都是执行的父类的方法,此时可以确定运行时无论f的对象是什么都是执行父类中的方法,绑定Fu类中的m(Object)方法。此为前期绑定,也叫静态绑定,是编译期时发生的绑定。
    4、如果方法不被static、final、private修饰,编译器采用动态绑定,记录方法签名m(Object)。
    运行期:
    执行f.m(1)字节码时
    1、如果是静态绑定,直接执行绑定的方法,不关心对象类型。
    2、如果是动态绑定,编译期只确定了方法签名,方法所在类未确定,根据f引用对象的实际类型,去该类型中找到签名为m(Object)的方法,执行该方法,此时运行时才把方法调用与具体方法体关联起来。

动态绑定就是运行的时候才决定执行哪个方法体,静态绑定在编译期就确定了要执行的方法体。
动态绑定在编译期确定执行的方法签名,在运行期确定执行哪个类的方法,实现自由执行子类重写的方法。静态绑定在编译期决定执行哪个类中的哪个方法,因为这些方法不能被重写,子类对象调用也是调用的父类中的,可以明确方法体,对于这类不能重写的方法,采用静态绑定性能更好,虽然采用动态绑定也能实现。

一个静态绑定的例子

public class Fu {private void m(){System.out.println("父类private方法执行");}public static void main(String[] args){Fu f = new Zi();f.m();}
}public class Zi extends Fu{public void m(){System.out.println("子类public方法执行");}public static void main(String[] args){Fu f = new Zi();// f.m(); 无法编译}
}output:
父类private方法执行

补充一个继承泛型类的例子

public class GenericClazz<T> {public void m(T t){System.out.println("泛型类中m()方法执行");}
}public class Zi extends GenericClazz<Integer>{public void m(Integer i){System.out.println("子类中m()方法执行");}//    public void m(Object i){
//        System.out.println("子类中m()方法执行");
//    } 无法编译public static void main(String[] args){GenericClazz f = new Zi();f.m(1);}
}output:
子类中m()方法执行

泛型类中,由于有泛型擦除,T实际会被在编译时替换为Object类型,那么Zi类继承GenericClazz类就是继承的m(Object)方法,而m(Integer)是对m(Object)的重载而不是重写,在用f.m(1)调用时,按照上面的理论,运行时应该是调用的Zi类中的m(Object)方法,而m(Object)子类没有重写,应该走父类语句,但是这里却走了子类的m(Integer)方法,原因是编译器自动在子类做了m(Object)方法的重写,会执行类似下面的语句:

public void m(Object t){m((Integer) t);
}

让m(Integer)看起来就像是重写的父类的m(Integer)一样,并且还关闭了子类对m(Object)的手动重写,可以看到上面手动重写m(Object)编译是报错的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/833192.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑神话吉吉国王版搞笑版总共4关(附下载链接)

话不多说,上图吉吉国王版黑神话点击下载

在Clion中快速生成函数中形参注释及添加函数说明

快速生成函数中形参注释 只需要在函数前输入/**,然后按回车,这样即可快速生成如下函数形参注释。 新增函数描述 在设置界面中的搜索框中输入Code Generation,然后勾选 如下选择框,这样就可以在如上生成的代码快中新增函数描述栏 说明:有些版本可能找不到,按如下位置查找…

痞子衡嵌入式:在i.MXRT启动头FDCB里配置串行NOR Flash多个寄存器的注意事项

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是在FDCB里配置串行NOR Flash多个寄存器的注意事项。关于使用 i.MXRT 启动头 FDCB 来设置 Flash 内部寄存器,痞子衡写过如下两篇文章,在进入本文之前,建议大家先阅读下这两篇文章,有个初步了解。《在FDCB里…

Nginx_基础

Nginx_基础 Nginx 基础一、Nginx 简介1.1 简介1.2 正向代理和反向代理 二、基本命令 三、配置格式3.1 基本配置格式3.2 时间和空间单位3.3 官方配置模板 四、部署静态网站4.1 增加配置4.2 检查配置4.3 重载配置 五、实现负载均衡5.1 部署后台服务5.2 负载均衡配置5.3 负载均衡策…

【Unity】对TMPAsset打包记录

TMPAsset中对于SourceFontFile引用,不会打包到AssetBundle中

4G核心网学习之4G EPS 中的 PDN Connection

PDN PDN(Packet Data Network分组数据网络),严格意义上讲可以分为内部 PDN 和外部 PDN:内部 PDN 即 EPS 系统中的分组数据网络,是 EPS 系统实体(e.g. MME、HSS、SGW、PGW、PCRF)之间的网络通信;而外部 PDN 即 EPS 系统之外的分组数据网络,例如:3GPP 网络 CDMA1X、Int…

spring初始学习

开始学习了容器 bean 依赖注入 Spring框架是一个开源的Java平台,它提供了全面的基础设施支持,以便你可以更容易地开发Java应用程序。以下是Spring框架中关于容器、Bean和依赖注入的一些基础知识点:Spring容器(Spring Container) Spring容器是Spring框架的核心,负责实例化…

sherpa-onnx:跨平台、多语言的语音处理工具包

Sherpa-onnx 是一个基于 ONNX 运行时的开源语音处理库,支持多种语音相关任务,包括语音识别、语音合成、说话人识别、语言识别等。它不仅支持多种编程语言(如 C++、C、Python、JavaScript、Java、C# 等),还支持多种操作系统和硬件平台(如 Windows、macOS、Linux、Android、…

基于FPGA的1024QAM基带通信系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR

1.算法仿真效果 vivado2019.2仿真结果如下(完整代码运行后无水印):设置SNR=40db将数据导入matlab显示星座图:设置SNR=35db将数据导入matlab显示星座图:仿真操作步骤可参考程序配套的操作视频。2.算法涉及理论知识概要1024QAM是一种高级调制方式,可以携带更多的信息位(10…

Hive的分区和排序

一、Hive的分区(十分重要) 1、分区是什么 答:我们可以把一个大的文件分隔成一个个小的文件,这样每次操作一个小文件就很方便了 2、为什么要进行分区 答:通过分区,当我们查询的时候,可以只扫描与条件相关的分区,这样做,避免了全局扫描,加快查询速度 1、静态分区(SP) 静…

项目冲刺4-3

仓库地址:https://github.com/bitpurleclude/GDUT-Goofish.git这个作业属于哪个课程 (https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/join?id=CfDJ8AOXHS93SCdEnLt5HW8VuxT_rAhbZKO3TfjMmbv1B0Re5Fp2d0_QACha2ZoYZ4fxF-ZKCCAhfJl7B8mvCfesLYE02X8T6kx_2R8w0SR-ykrgDVRKW…

【Linux】git note -v command not found

安装note.js Node.Js中文网 『Win+E』『此电脑』右键点击『属性』『高级系统设置』-『环境变量』『系统变量』-『NOTE_PATH』“C:\Program Files\nodejs”『用户变量』-『Path』“C:\Program Files\nodejs”『Win+R』重新启动控制台『cmd』-“$NOTE -v” 双击重新启动『Git Ba…