Java动态方法调用疑问:同参数同对象为何调用结果不同?
拆解Java方法调用的重载与重写逻辑
这问题的核心其实是搞懂Java里方法重载(Overload)是编译时确定,而方法重写(Override)是运行时确定的规则,咱们一个个拆解你存疑的四个调用:
先明确所有变量的编译时/运行时类型:
A ac = c;:编译时类型是A,运行时实际对象是CA ab = b;:编译时类型是A,运行时实际对象是BAA aac = c;:编译时类型是AA,运行时实际对象是C
1. ac.f(b) → 输出4
编译阶段:ac的类型是A,而A接口只定义了f(A x)这一个方法签名。不管参数b的实际类型是B,编译时只会匹配A里存在的方法,也就是f(A)。
运行阶段:实际对象是C,C重写了f(A x)方法,所以执行C的f(A)实现,输出4。
2. ab.f(b) → 输出1(重点解释)
这是最容易踩坑的点:
- 编译阶段:
ab的类型是A,A里只有f(A x),所以编译时只能绑定到f(A)这个方法签名。 - 运行阶段:实际对象是
B,但B的f(B x)是新增的重载方法,不是对A接口方法的重写——因为A里根本没有f(B x)这个方法签名。所以运行时会执行B中对f(A x)的实现,也就是输出1。
对比b.f(b)输出2的原因:当用B类型的引用调用时,编译时能看到B里的f(B x)重载,会优先匹配参数类型更精确的方法,所以走f(B)输出2。这就是为什么同对象同参数,引用类型不同结果不同的关键——编译时能看到的方法签名不一样。
3. aac.f(b) → 输出5
编译阶段:aac的类型是AA,AA继承A的同时新增了f(B x)方法。参数b是B类型,编译时会匹配到更精确的f(B x)方法签名。
运行阶段:实际对象是C,C实现了AA的f(B x)方法,所以执行C的f(B)实现,输出5。
4. aac.f(c) → 输出4
编译阶段:aac的类型是AA,AA里的方法是f(A x)(继承自A)和f(B x)。参数c是C类型,C是A的子类但不是B的子类,所以编译时只能匹配到f(A x)。
运行阶段:实际对象是C,执行C的f(A)实现,输出4。
核心规则总结
- 重载:由编译时的引用类型和参数的编译时类型决定,编译器会选择最匹配的方法签名
- 重写:由运行时的实际对象类型决定,执行该对象对方法的具体实现
内容的提问来源于stack exchange,提问作者ddominnik
相关产品推荐
相关产品推荐

