You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java动态绑定问题:调用computeDiscount方法为何最终输出结果为5?

问题分析

这个问题本质是混淆了Java中方法重载的静态绑定和方法重写的动态绑定的执行逻辑,我们一步步拆解就能得到结果:

// 代码核心结构
class Customer { }
class RegisteredCustomer extends Customer{}

class Shop {
    public int computeDiscount(Customer c){return 0;}
}

class OnlineShop extends Shop {
    // 重写父类方法,签名一致:computeDiscount(Customer)
    public int computeDiscount(Customer c){return 5;}
    // 重载方法,新签名:computeDiscount(RegisteredCustomer)
    public int computeDiscount(RegisteredCustomer c){return 15;}
}

class OnlinePremiumShop extends OnlineShop{
    // 重载父类方法,签名:computeDiscount(RegisteredCustomer)
    public int computeDiscount(RegisteredCustomer c){return 20+super.computeDiscount(c);}
}

// main方法
public static void main(String[] args) {
    RegisteredCustomer c3 = new RegisteredCustomer();
    Shop s2 = new OnlinePremiumShop();
    System.out.println(s2.computeDiscount(c3)); // 输出5
}

核心概念区分

先理清两个绑定规则,这是问题的核心:

  • 方法重载(Overload):同一个类中方法名相同、参数列表不同的方法,属于编译期静态绑定,只根据调用对象的*静态类型(声明时的类型)*和参数的静态类型确定要调用的方法签名,和运行期的实际对象类型无关
  • 方法重写(Override):子类定义和父类方法名、参数列表、返回值完全一致的方法,属于运行期动态绑定,根据调用对象的*动态类型(实际指向的对象类型)*决定最终执行哪个类的实现

执行过程拆解

第一步:编译期确定方法签名

调用方法的对象是s2,它的静态类型是Shop,编译器只会在Shop类的定义中查找匹配的computeDiscount方法:
Shop类中只有1个computeDiscount方法,签名为computeDiscount(Customer c),即使传入的参数c3静态类型是RegisteredCustomer(Customer的子类,能向上转型匹配),编译期最终确定的调用签名就是computeDiscount(Customer c)。

注意:子类OnlineShop、OnlinePremiumShop中定义的computeDiscount(RegisteredCustomer c)是全新的重载签名,父类Shop中没有这个签名的方法,编译期完全不会考虑这些重载方法。

第二步:运行期匹配对应签名的重写实现

运行期s2的动态类型是OnlinePremiumShop,我们只需要查找computeDiscount(Customer c)这个签名的重写实现即可:

  1. OnlinePremiumShop没有重写computeDiscount(Customer c)这个签名的方法,只定义了参数为RegisteredCustomer的重载方法,不符合匹配要求
  2. 向上查找父类OnlineShop,OnlineShop重写了computeDiscount(Customer c)方法,返回值为5
  3. 最终执行这个实现,输出结果为5

通用分析步骤(避免判断错误)

以后遇到类似多态方法调用的问题,严格按以下4步分析即可:

  1. 找到调用方法的对象,先看它的静态类型(等于变量声明时左边的类型),在这个类型的定义中,找出所有和调用方法名相同、参数个数匹配的方法,选择适配参数静态类型的最匹配签名,这就是编译期确定的最终调用签名,子类的重载方法这一步直接忽略
  2. 再确定这个调用对象的动态类型(等于变量赋值时右边new的类型)
  3. 从这个动态类型开始,依次向上查找父类,找有没有对第一步得到的完全相同签名的方法的重写
  4. 找到继承链中最下层的重写实现,执行结果就是最终输出

扩展验证:如果把s2的声明改成OnlineShop s2 = new OnlinePremiumShop();,编译期就能在OnlineShop中匹配到computeDiscount(RegisteredCustomer c)的签名,运行期就会调用OnlinePremiumShop的对应实现,输出20+15=35。

内容的提问来源于stack exchange,提问作者ctrlaltdel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 18:15:03