Java 前期绑定与后期绑定

在 Java 面向对象的三大特征封装、继承、多态中,多态对于刚接触的人来说往往较难理解。理解它的原理有助于我们更深一步的认识。

我们知道,Java 中的多态表现为同一个行为具有多个不同表现形式,使得我们可以通过父类的引用指向子类的方法,比如下面这样:

class Father {
    public void g() {
        System.out.println("father's g()");
    }
}
    
class Son extends Father {
    public void g() {
        System.out.println("son's g()");
    }
}
    
public class Polymorphic {
    public static void main(String[] args) {
        Father f = new Son();
        f.g();
    }
}

输出结果:

son's g()

那程序是怎么知道调用的是子类的方法呢?


绑定

绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对 Java 来说,绑定分为静态绑定和动态绑定,或者叫做前期绑定和后期绑定。

  • 静态绑定:在程序执行前方法已经被绑定(也就是说在编译过程中就已经知道这个方法到底是哪个类中的方法),由编译器实现。C就是典型的前期绑定。Java 中 final,static,private 修饰的方法和构造方法是前期绑定
  • 动态绑定:运行时根据具体对象的类型进行绑定。

在 Java 编程思想中,作者将多态称作动态绑定;在其他一些地方,将方法重载(overload)称为编译时多态,方法覆盖(override)称为运行时多态(编译时与运行时)。大家只要理解了原理即可,不必过分纠结于概念。


加载过程

Java 代码首先会通过 javac 编译成字节码文件,此时代码并没有放进内存。当需要类加载的时候,JVM 会执行加载类的全过程,包括

  • 加载
  • 验证
  • 准备
  • 解析
  • 初始化

动态绑定发生在加载类的解析阶段。

现在假设当前代码所处的类为D,要把一个从未解析过的符号引用N解析为一个类或接口C(可以理解为代码里的Son对象)的直接引用(参考深入理解Java虚拟机第二版),步骤如下:

  1. 如果C不是数组类型,虚拟机将会把代表N的全限定名传递给D的类加载器去加载这个类C。如果加载过程出现任何异常,则解析失败。
  2. 如果C是数组类型,并且数组元素类型为对象,即N的描述符是类似 “[Ljava/lang/Integer”的形式,按照第一点的规则加载数组元素类型,接着由虚拟机生成一个代表此数组维度和元素的数组对象。
  3. 如果上面步骤没有出现异常,则C在虚拟机中已经成为一个有效的类或接口,之后会进行符号引用验证,确认D是否具备对C的访问权限,否则抛出异常。

常见概念解释:

  • 符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
  • 全限定名:如果一个类叫 Hello,所在的包为 com.lslxy,则其全限定名为 com/lslxy/hello
  • 简单名称:没有类型和参数修饰的方法或者字段名称
  • 描述符:字段的数据类型、方法的参数列表(包括数量,类型及顺序)和返回值
  • 方法签名:Java 代码中的方法签名包括方法名称、参数顺序和参数类型,字节码中的方法签名包括方法名称、参数顺序、参数类型、返回值、受查异常表
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值