为何向上转型对象会调用子类重写的父类方法?对JLS规则的困惑
关于Java向上转型方法调用的JLS规范误解解析
你混淆了Java方法调用的编译阶段签名确定和运行时方法选择两个独立步骤,JLS 15.12.1只负责前者,后者有单独的规范约束。
1. 编译阶段:JLS 15.12.1的作用
JLS 15.12.1里提到的「搜索类型为变量声明类型」,是编译时用来确定方法签名的规则:
- 当你写
superclass.Identifier()时,编译器会基于superclass的声明类型Superclass去查找匹配的方法(方法名+参数列表),确保这个方法在Superclass中是可访问、存在的,否则会直接编译报错。 - 这一步不关心变量实际指向的对象类型,只做语法和合法性检查。
2. 运行阶段:实际方法的选择规则
编译通过后,运行时会根据对象的实际类型来选择要执行的方法,对应的规范是JLS 15.12.4.4(运行时方法选择):
- 对于实例方法调用,JVM会从实际对象的类型开始,向上遍历继承链,找到第一个重写了编译阶段确定的签名的方法,然后执行它。
- 比如你例子里
Superclass superclass = new Subclass();,实际对象是Subclass类型,所以会优先执行Subclass中重写的方法,哪怕变量声明类型是Superclass。
结合你给出的示例理解
拿JLS 8.4.8.1的Point和SlowPoint举例:
- 编译时:如果代码是
Point p = new SlowPoint(); p.move(5,5);,编译器会基于Point的类型查找move(int, int)方法,确认存在,编译通过。 - 运行时:JVM发现实际对象是
SlowPoint,而SlowPoint重写了move(int, int),所以执行SlowPoint里的方法,而不是Point的原方法。
总结
JLS 15.12.1和方法重写的运行时行为并不矛盾:前者管「编译时能不能找到合法的方法签名」,后者管「运行时到底执行哪个类的方法」。你之前只看了编译阶段的规则,忽略了运行时的方法选择逻辑,所以才产生了困惑。
内容的提问来源于stack exchange,提问作者user24723440
相关产品推荐
相关产品推荐

