Java方法签名由方法名和参数类型列表(含顺序)组成,不包含返回值、异常、修饰符;泛型因类型擦除、可变参数因转为数组,易导致签名冲突。

Java方法签名到底由什么组成
Java里判断两个方法是不是同一个,只看方法名加参数类型列表(顺序和类型),不看返回值、异常、修饰符。也就是说,String getValue() 和 int getValue() 在同一个类里能同时存在——编译器不认为它们冲突。
常见错误现象:改了返回值类型以为重载了,结果编译报错 duplicate method;或者想靠 throws 不同来区分方法,也失败。
-
void print(String s)和void print(Object o)是不同签名(参数类型不同) -
void handle(List<string> list)</string>和void handle(List<integer> list)</integer>是相同签名(泛型擦除后都是List) -
void post(int id, String name)和void post(String name, int id)是不同签名(参数顺序不同)
为什么泛型和可变参数容易踩坑
泛型在字节码里被擦除,所以 get(List<string>)</string> 和 get(List<integer>)</integer> 编译后都是 get(List),不能共存。可变参数本质是数组,foo(String...) 实际签名是 foo(String[]),因此和 foo(String[]) 直接冲突。
使用场景:写工具类时想提供多种泛型友好入口,或封装日志/校验等通用逻辑,常会无意识写出无法编译的“伪重载”。
立即学习“Java免费学习笔记(深入)”;
- 别写
process(List<t>)</t>和process(Set<t>)</t>同时存在——擦除后都是process(Collection) - 避免同时声明
log(String...)和log(String[]),JVM 认为重复 - 如果真需要多态行为,用不同方法名,比如
logArgs(String...)和logArray(String[])
重载解析发生在编译期,不是运行期
哪个重载方法被调用,完全由编译时的实参类型决定,跟实际对象类型无关。这导致很多“看似合理”的调用结果出人意料。
性能影响:无运行时开销,但过度依赖重载会让代码可读性下降,尤其配合自动装箱、隐式转换时。
-
doSomething(1)会匹配doSomething(int),而不是doSomething(Integer)或doSomething(long) -
Object obj = new String("a"); doSomething(obj);—— 即使obj运行时是String,也只会调用doSomething(Object) - 自动装箱可能引发歧义:
method(int)和method(Integer)同时存在时,传5优先选int;但传null就只能进Integer分支
IDE 和 javap 怎么验证方法签名
光看源码容易误判,尤其涉及泛型、桥接方法、lambda 生成的方法时。最可靠的方式是看编译后的字节码签名。
使用场景:排查“明明写了不同方法却编译不过”,或阅读反编译代码时理解真实调用链。
- 用
javap -s查看签名:比如javap -s MyClass会显示类似(Ljava/lang/String;)V这样的 JVM 描述符 - IDE 中按 Ctrl+Click(IntelliJ)跳转到方法定义时,注意看右上角是否标着 “bridge method” 或 “synthetic”
- 在 IntelliJ 的 Structure 视图里,同名方法若参数列表视觉一样,基本就是签名冲突了









