Java 方法与重载
Java 方法声明、参数、返回值、static 与重载解析:如何把行为切成可复用、可测试的单元。
#concept
#growing
#dev
#java
[!info] related notes
Java 方法与重载
这个概念为什么出现
若所有逻辑堆在 main,无法测试、无法复用、无法命名意图。方法把行为变成:
- 有名字的业务动作
- 有明确输入输出的边界
- 可被重载以服务不同参数形态
还要分清:重载(overload) 是同名方法具有不同参数签名(也可涉及继承来的方法);覆盖(override) 是子类重写父类方法——后者在继承篇。
[!abstract] 一句话理解 方法是类中的可执行成员;签名(名称+参数列表)决定重载选择,
static方法属于类而不绑定实例,返回类型不是重载区分依据。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:定价服务提供“按件数”和“按件数+优惠码”两种报价入口
调用方有时只有件数,有时带优惠码。用重载表达同一业务动作的不同输入,而不是 price1/price2 乱命名。
public class PricingService {
// price:基础报价(分)。
//
// 业务意图:单价 100 分 * 件数。
// 教学点:实例方法可复用内部规则。
int price(int units) {
if (units < 0) {
throw new IllegalArgumentException("units < 0");
}
return Math.multiplyExact(units, 100); // 超出 int 分值范围时显式失败
}
// 示例规则:任意非空白优惠码减 50 分,但不低于 0;未实现真实优惠码校验。
// 教学点:参数列表不同 → 合法重载;返回类型相同与否不是关键。
int price(int units, String coupon) {
int base = price(units); // 复用基础规则,避免复制粘贴
if (coupon == null || coupon.isBlank()) {
return base;
}
int discounted = base - 50;
return Math.max(discounted, 0);
}
// formatMoney:静态工具方法——不依赖账户状态。
static String formatMoney(int cents) {
return cents + " cents";
}
public static void main(String[] args) {
PricingService svc = new PricingService();
System.out.println(formatMoney(svc.price(2)));
System.out.println(formatMoney(svc.price(2, "SAVE")));
}
}
建议运行:
javac PricingService.java && java PricingService
期望输出:
200 cents
150 cents
结合场景再看三个关注点
- 重载是编译期选择,依据实参静态类型。
- 复用基础方法避免两套规则漂移。
- static 适合无实例状态工具;强行 static 一切会毁掉可测试多态。
核心概念与准确模型
1. 方法声明要素
- 修饰符(
public/private/static等) - 返回类型或
void - 名称
- 参数列表
- 抛出声明(异常篇)
- 方法体
2. 签名与重载
- 入门看 方法名 + 形参类型列表(含顺序);泛型签名还涉及类型参数,不能仅凭
List<String>与List<Integer>重载(擦除后冲突)。 - 返回类型不同不足以构成重载
- 仅参数名称不同不构成重载
3. 传参
- Java 参数传递是值传递
- 基本类型传值副本
- 引用类型传引用副本(可修改同一对象,不能让调用方变量改指向——除非返回/包装)
4. 可变参数
void log(String... lines)
String...在方法内就是 String[],且只能位于最后一个参数位置;调用方可传多个参数或直接传数组。f(String...)与f(String[])不能并存为重载;零实参通常得到空数组,显式传(String[]) null则得到 null。
5. 实例方法 vs static
| 实例方法 | static 方法 | |
|---|---|---|
| 调用 | 需实例(通常) | 类名或实例名(推荐类名) |
| 访问 | 可访问实例字段 | 不能直接访问实例字段 |
| 多态 | 符合继承/访问规则且非 final 的方法可覆盖 | 同名同签名可隐藏,不按接收对象运行时类型分派 |
边界情况与反直觉行为
- 重载分阶段匹配:先尝试不依赖装箱或变参展开的固定参数调用,再尝试装箱,最后变参;例如 int 实参在
f(long)与f(Integer)之间选择前者。 - null 实参 在多个引用类型重载间可能编译失败(歧义)。
- 递归 别忘终止条件。
常见误区
[!warning] 常见误区:重载 = 覆盖 错误理解:子类同名不同参叫覆盖。
正确模型:不同参是重载;同签名实例方法替换分派才是覆盖。
为何易错:英文 overload/override 接近。
工程实践
- 方法名用动词短语:
calculateFreight、reserveStock。 - 参数个数过多时引入参数对象。
- 保持方法短小,一个主路径意图。
- 对 public 重载做测试矩阵,防止解析意外。
本节总结
- 方法是可测试行为单元
- 重载靠参数列表区分,编译期解析
- static 与实例成员职责不同
- 为继承篇的 override 打好对比基础
自测题
- 为什么只有返回类型不同不能重载?
- 引用类型参数被方法改字段,调用方为何看得到变化?
参考答案
- 调用处可忽略返回值,编译器无法稳定仅凭返回类型消歧。
- 传递的是引用副本,仍指向同一对象,对象字段变更双方可见。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| JLS Methods | 规范 | 方法声明 |
| JLS Overloading | 规范 | 方法调用与解析 |