升级一个公共 SDK 后,原来正常的方法调用突然走了另一个重载,你会怎么解释和排查?
我会先控制影响,再按证据定位:重载发生在同一个类型体系中,方法名相同但参数列表不同,编译器根据引用的静态类型和实参选择方法;重写发生在父子类之间,子类提供相同签名的实例方法,运行期根据对象实际类型动态分派。 仅修改返回类型不能构成重载。重写可以使用协变返回类型,访问权限不能更严格,抛出的受检异常范围不能比父方法更宽。
我会先确认影响范围,同时控制故障继续放大。理解编译期重载选择、运行期动态绑定以及重写的类型约束。重载发生在同一个类型体系中,方法名相同但参数列表不同,编译器根据引用的静态类型和实参选择方法;重写发生在父子类之间,子类提供相同签名的实例方法,运行期根据对象实际类型动态分派。 仅修改返回类型不能构成重载。重写可以使用协变返回类型,访问权限不能更严格,抛出的受检异常范围不能比父方法更宽。
止损之后,我会按请求链路建立证据,而不是凭经验猜。沿着「收集候选方法 → 编译期选择重载 → 必要时装箱或提升 → 运行期选择重写实现 → 返回结果或抛出异常」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 收集候选方法 重载候选由方法名和可见范围确定,返回类型不能单独区分两个重载。 编译期选择重载 编译器依据变量的静态类型和实参类型选择最具体签名,因此重载是早绑定。 javap invokevirtual/invokestatic:区分运行期虚分派与静态调用。 java.lang.Class#getMethods:桥接、继承与重写后的可见方法。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。
定位时我最关注这些参数、指标和容量关系。编译包含 null、装箱、可变参数和父类静态类型的调用矩阵,用 javap 确认描述符;升级 API 前跑二进制兼容检查。
找到根因后先做最小修复,再用同样的流量验证。工具类同时提供 log(Object) 与 log(String...)。某调用传入 null 后编译器无法确定更具体目标,升级即编译失败;另一些调用因静态类型为 Object 落到非预期重载。解决方案是减少语义相近的重载,并在边界使用明确类型或不同方法名。 null 在多个引用重载间产生歧义:编译歧义与升级失败:语义不同的重载改为明确方法名。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「重载候选数」为主基线,记录值应满足「同语义控制在少量」;同时保存 编译歧义与升级失败、重载数量与调用分布,使后续变化能够回到同一时间轴比较。
恢复阶段要逐步放量,最后把监控和边界补齐。方案:更适合的场景:主要收益:代价与边界。 重载:同一动作对不同静态参数形态提供便利:调用自然、编译期发现:过多重载会产生歧义与兼容风险。 重写:子类替换父类行为:支持运行期多态:必须遵守里氏替换与父类契约。 策略对象:行为需要独立扩展和组合:避免继承层级与重载爆炸:调用方需显式选择或注入策略。 选型至少带上 对象创建率、调用频次、输入规模和 API 边界,并用上面的量化基线验证;未知数据应明确为待测假设。