前提准备: 方法的重载与重写
方法的重载: 在同一个类中定义名字相同的方法,那么它们的参数类型必须不同,这些方法之间的工具,我们称之为重载。重载的方法在编译过程中就可以完成识别,具体到每一个方法的调用,Java编译器会根据所传入参数的声明类型
来选取重载的方法,其选取的原则如下:
1.在不考虑对基本类型的自动装拆箱和变长参数列表的情况下选取重载方法
2.如果在1阶段中没有找到适配的方法,那么在允许自动装拆箱但不允许可变长参数的情况下选取重载方法
3.如果在2阶段中没有找到适配的方法,那么允许自动装拆箱以及可变长参数的情况下选取重载方法
如果java编译器在同一个阶段中找到了多个适配的方法,那么它会在其中选择一个最为”贴切“的,这个“贴切“程度的一个关键形式是参数类型的继承关系。
其实除了同一个类中的方法,重载也可以作用于这个类所继承来的方法。
下面思考一个问题:如果子类定义了与父类中非私有方法同名的方法,而且这两个方法的参数类型相同,那么这两个方法之间又是什么关系?
如果这两个方法都是静态的,那么子类的方法会隐藏父类的方法,如果这两个方法不是静态的,而且不是私有的,那么子类中的方法重写了父类中的方法。
java是一门面向对象的语言,它的一个重要的特性就是多态。而方法的重写,正是多态最重要的一种体现方式,它允许子类在继承父类部分功能的同时,拥有自己独特的行为。对于重写的调用,它会根据调用者的动态类型来确定实际调用的方法。
JVM中的静态绑定与动态绑定
对于java虚拟机而言,它是怎么识别方法的呢?
1.java虚拟机识别方法的关键:
- 类名
- 方法名
- 方法描述符
2.方法描述符包含哪些内容?
- 方法参数
- 方法的返回值类型
在同一个类中,如果同时出现多个名字相同并且方法描述符也相同的方法,那么java虚拟机会在类的验证阶段报错。
Java虚拟机中对于方法重写的判定同样基于方法描述符。如果子类定义了与父类中非私有、非静态方法同名的方法,那么只有当这两个方法的返回值类型和参数类型一致,java虚拟机才会判定是重写。
由于对重载方法的区分在编译阶段已经完成,我们可以认为java虚拟机中不存在重载这一个概念。
确切的说,java虚拟机中的静态绑定指的是在解析时便能够直接识别目标方法的情况,而动态绑定指的是需要在运行过程中根据调用着的动态类型识别目标方法的情况。
调用过程的符号引用
在编译过程中,我们并不知道目标方法的具体的内存地址.因此,java编译器会暂时使用符号引用来表示该目标方法。这个符号引用包括目标方法所在的类或者接口的名字,以及目标方法的方法名和方法描述符。符号引用 存储在class文件的常量池中,根据目标方法是否为接口方法可以分为接口符号引用和非接口符号引用。
利用 javap - v 可以打印某个类的常量池。
在执行使用了符号引用的字节码之前,java虚拟机需要解析这些符号引用并替换为实际引用。
<u>对于非接口符号引用,假如该符号引用指向的类为C,则java虚拟机会按照如下步骤进行查找:</u>
- 在C中查找符合名字及描述符的方法
- 如果没有找到,在父类中继续搜索,直至Object类
- 如果没有找到,在C所有实现或间接实现的接口中搜索,这一步搜索得到的目标方法必须是非私有非静态的。并且如果目标方法在间接实现的接口中,则需要满足C与该接口之间没有其他符合条件的目标方法.如果存在多个符合条件的目标方法,则任意返回其中一个。
从这个解析算法可以看出,静态方法也可以通过子类来调用。此外,子类的静态方法还会隐藏父类中同名同描述符的静态方法。
对于接口符号引用,假如该接口符号引用所指向的接口为I,则java虚拟机会按照如下的步骤进行查找:
- 在I中查找符合名字以及描述符的方法
- 如果没有找到,在Object类中的公有实例方法中搜索
- 如果没有找到,在I的超接口中搜索,这一步的搜索结果的要求与非接口符号引用步骤3的要求一致
经过上述的解析步骤之后,符号引用会被解析成实际引用。对于可以静态绑定的方法调用而言,实际引用的是一个
指向方法的指针。对于需要动态绑定的方法调用而言,实际引用则是一个方法的索引。
以上内容来自于极客时间郑雨迪老师<<深入拆解Java虚拟机>>的总结,感兴趣的小伙伴可以加入一起学习哈~~
网友评论