Java 方法:签名、值传递、重载与 varargs
更新时间:2026-09-20。值传递的完整论证见 Java 是值传递还是引用传递,用 javap 可以直接看到方法签名的内部描述符。
一、方法签名到底包含什么
方法名 + 形参列表(个数、类型、顺序)决定签名;返回类型、参数名、异常声明不参与。所以:
java
void f(int a) {}
int f(int b) { return 1; } // 编译错误:参数列表相同,与返回值无关,重复定义
void f(long a) {} // 合法:参数类型不同,构成重载二、按值传递(pass by value)
Java 只有一种传递方式——值传递:方法拿到的是实参的一个副本。
java
void reassign(User u) { u = new User("B"); } // 改的是副本指向,外面不变
void rename(User u) { u.setName("B"); } // 副本和原引用指向同一对象,内容被改
void inc(int x) { x++; } // 基本类型副本自增,外面不变- 基本类型:传值的拷贝,方法内改不影响外面;
- 引用类型:传「引用值(地址)」的拷贝,副本与原引用指向同一个堆对象,所以能改对象内容,但在方法内把副本
=指向新对象不会影响调用方。
三、重载(overload):编译期就定好调用哪个
同名方法、不同参数列表即重载。调用时编译器按下面顺序选最具体的:
java
void m(long x) { System.out.println("long"); }
void m(Integer x) { System.out.println("Integer"); }
void m(int... x) { System.out.println("varargs"); }
m(1); // long:先尝试不用装箱/不展开 varargs 的类型提升匹配优先级:
- 精确类型;
- 自动类型提升(int→long→float→double 等,子类→父类);
- 自动装箱/拆箱(int↔Integer);
- 可变参数(优先级最低)。
重载是静态(编译期)分派,按声明类型选;而重写(override)是运行期按实际对象动态分派,两者不要混。
java
void g(Object o) {}
void g(String s) {}
Object o = "x";
g(o); // 调 g(Object):编译期 o 的声明类型是 Object,即使运行时是 String四、可变参数 varargs
java
int sum(int... nums) { // 本质:int[] nums
int s = 0;
for (int n : nums) s += n;
return s;
}
sum(); // 0 个参数 → 空数组(不是 null)
sum(1, 2, 3); // 编译器把它们收集成数组
sum(new int[]{1,2}); // 也可直接传数组规则与坑:
- 一个方法最多一个 varargs,且必须在最后一个参数:
f(String s, int... x); - 传 0 个参数得到空数组;
- 重载 + varargs 容易产生歧义(
m(int...)与m(Integer...)同时存在时某些调用不明确),能不混用就别混用; - 泛型 varargs 与
Object...混用会有堆污染警告(@SafeVarargs)。
五、递归与方法调用栈
每调一次方法就在 JVM 栈压一个栈帧(局部变量/操作数栈/返回地址),递归过深或无终止条件抛 StackOverflowError。线程栈大小可用 -Xss 调,但先应检查递归是否该改成迭代或尾逻辑。
总结
Java 方法签名只由方法名和参数类型列表决定、与返回值无关;传参永远是值传递,引用类型传的是地址副本,所以能改对象内容、但方法内重新指向影响不了调用方;重载是编译期静态分派,匹配顺序为精确类型→类型提升→装箱→可变参数,按变量声明类型而非运行时对象选择;varargs 本质是数组、只能放最后、可传零个(空数组)且易与重载产生歧义;方法调用在 JVM 栈压栈帧,递归无出口会 StackOverflowError。