核心是确认运行时实际执行的方法及委托路径,需用Ctrl+Alt+B跳转实现、在入口查this.getClass()、从调用栈识别代理与反射、临时加getClass()日志验证。

处理Java继承链中多层同名方法重写导致的堆栈轨迹追踪,核心不是“看调用栈有多长”,而是快速确认“当前执行的是哪个类的方法”以及“它内部调用的又是谁的实现”。静态继承图容易误导,运行时实际类型和委托路径才是关键。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
跳转到实现,而非声明
在方法调用处(比如 service.process()),别按 Ctrl+Click 跳去父类声明,改用 IntelliJ 的 Ctrl+Alt+B 或 VS Code(配 Java 扩展)的等效操作,直接跳转到运行时真正执行的方法体。IDE 会列出所有可能实现——如果弹出多个选项,说明此处存在多态歧义,正是需要排查的位置。这对抽象方法、@Override 方法和接口 default 方法尤其有效。
入口处查 this.getClass()
不要只在顶层父类方法里设断点。在最外层被触发的方法(如 doWork())入口加断点,Debug 时直接查看 this.getClass().getName()。也可以在断点处添加日志表达式:
System.out.println("Running as: " + this.getClass().getSimpleName());
这样一眼就能确认真实类型,避免靠继承图“猜”执行路径。
从调用栈反向识别代理与反射
断点命中后,展开左侧调用栈,逐层向上看:
• 每一帧的类名是否符合你预期的实际类型?
• 是否出现类似 UserServiceImpl$$EnhancerBySpringCGLIB$$abc123 这样的类名?这是 Spring AOP、事务或缓存生成的代理类,源码不可见,需检查是否启用了相关增强。
• 栈中是否有 super.xxx() 或 Method.invoke()?这表示逻辑已脱离静态继承链,进入显式委托或反射调用,此时堆栈轨迹会突然“跳变”,需切换排查思路。
临时加 getClass() 日志验证分支
当 IDE 跳转失效、远程调试受限,或动态行为太复杂时,插入轻量日志比读十遍继承图更可靠:
• 放在构造器里,确认实例化类型;
• 放在重写方法第一行,确认是否被调用;
• 放在 if (this instanceof Xxx) 块内,验证类型判断是否生效。
日志成本极低,上线前删掉即可,不改架构也不加依赖。

















