方法表是JVM中实现运行时多态的核心数据结构,每个类在加载时构建固定指针数组,按继承链排列虚方法,子类重写时保持相同偏移量指向自身实现,支撑invokevirtual高效动态绑定。

方法区里的方法表,是Java实现运行时多态真正落地的关键。它不是概念模型,而是JVM内部真实存在的、被每个类维护的指针数组,直接支撑着invokevirtual指令的执行。
方法表存哪儿、长啥样
每个类加载进JVM后,其类型信息(包括字段、常量、字节码等)被存放在方法区;其中就包含一张方法表,由JVM在类加载阶段构建并固定下来。
- 表中每一项对应一个可被虚调用的实例方法(不包括
static、private、final方法) - 顺序按继承链排列:父类方法在前,子类新增或重写的方法紧随其后
- 若子类重写了父类方法,两者在各自方法表中占据**相同偏移量**,但指向各自的字节码入口
动态绑定怎么靠它完成
当执行animal.sound()这类调用时,JVM并不直接查对象本身,而是走一套确定路径:
- 先根据编译时类型(这里是
Animal)查它的方法表,拿到sound()在表中的索引位置 - 再根据实际对象的运行时类型(比如
Dog),定位到Dog类的方法表 - 用同一个索引去查
Dog的方法表,拿到真正要执行的Dog.sound()地址
这个“同索引、不同实现”的设计,让重写天然生效,无需每次遍历继承链查找。
为什么接口调用不直接用方法表
接口方法没有单继承链,一个类可能实现多个接口,且接口方法可以被任意类实现,无法预先排好固定偏移。
- 所以
invokeinterface不依赖单一方法表,而是用接口方法表(itable)+ 运行时搜索机制 - itable结构更松散,每个接口方法记录实现类中对应方法的实际地址,查找开销略高
- 这也是为什么接口多态比类继承多态稍慢一点的底层原因
方法表和你写的代码怎么呼应
你写的每处重写,都会直接影响方法表的生成结果:
- 没重写:子类方法表里该位置仍指向父类实现
- 重写了:子类方法表同一位置换成自己方法的入口地址
- 新增方法:追加到方法表末尾,不影响已有偏移
- 删掉重写又恢复父类行为?不行——方法表一旦生成就不会回滚,除非重新加载类
换句话说,多态不是靠“猜”或“反射”实现的,而是靠这张静态构建、运行时高效索引的表撑起来的。

















