Appearance
第5课:方法
🎯 学习目标
- 掌握方法的定义和调用
- 理解参数传递机制
- 掌握方法重载
- 理解递归
- 掌握可变参数
📖 一、方法基础
C vs Java 方法
C 语言:
c
// C: 函数
int add(int a, int b) {
return a + b;
}Java:
java
// Java: 方法(必须在类中)
public class Calculator {
public static int add(int a, int b) {
return a + b;
}
}1. 方法定义
java
// 语法
修饰符 返回类型 方法名(参数列表) {
方法体;
return 返回值; // 如果有返回值
}
// 示例
public static int add(int a, int b) {
return a + b;
}
// 无返回值
public static void printMessage(String msg) {
System.out.println(msg);
}2. 方法调用
java
public class MethodDemo {
public static void main(String[] args) {
// 调用有返回值的方法
int sum = add(10, 20);
System.out.println(sum); // 30
// 调用无返回值的方法
printMessage("Hello");
}
public static int add(int a, int b) {
return a + b;
}
public static void printMessage(String msg) {
System.out.println(msg);
}
}📖 二、参数传递
1. 基本类型参数(值传递)
java
public class ParamTest {
public static void main(String[] args) {
int x = 10;
change(x);
System.out.println(x); // 10(不变)
}
public static void change(int a) {
a = 20; // 只改变副本
}
}原理:传递的是值的副本,修改副本不影响原值
2. 引用类型参数(引用传递)
java
public class ParamTest {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
change(arr);
System.out.println(arr[0]); // 100(改变了)
}
public static void change(int[] array) {
array[0] = 100; // 修改的是同一个对象
}
}原理:传递的是引用的副本,但指向同一个对象
3. String 的特殊性
java
public static void main(String[] args) {
String str = "Hello";
change(str);
System.out.println(str); // "Hello"(不变)
}
public static void change(String s) {
s = "World"; // String 不可变
}📖 三、方法重载
1. 重载规则
java
public class Calculator {
// 方法1:两个 int 参数
public static int add(int a, int b) {
return a + b;
}
// 方法2:三个 int 参数
public static int add(int a, int b, int c) {
return a + b + c;
}
// 方法3:两个 double 参数
public static double add(double a, double b) {
return a + b;
}
// 使用
public static void main(String[] args) {
System.out.println(add(1, 2)); // 3
System.out.println(add(1, 2, 3)); // 6
System.out.println(add(1.5, 2.5)); // 4.0
}
}重载条件:
- ✅ 方法名相同
- ✅ 参数列表不同(个数、类型或顺序)
- ❌ 返回类型不同不算重载
java
// ❌ 编译错误:不算重载
public static int method() { return 1; }
public static double method() { return 1.0; } // 错误2. 重载的自动类型转换
java
public class Test {
public static void method(int x) {
System.out.println("int: " + x);
}
public static void method(double x) {
System.out.println("double: " + x);
}
public static void main(String[] args) {
method(10); // int: 10
method(10.5); // double: 10.5
byte b = 5;
method(b); // int: 5(byte 自动转 int)
}
}📖 四、递归
1. 递归基础
java
// 阶乘:n! = n × (n-1)!
public static int factorial(int n) {
if (n == 1) { // 递归出口
return 1;
}
return n * factorial(n - 1); // 递归调用
}
// 使用
int result = factorial(5); // 120执行过程:
factorial(5)
= 5 * factorial(4)
= 5 * (4 * factorial(3))
= 5 * (4 * (3 * factorial(2)))
= 5 * (4 * (3 * (2 * factorial(1))))
= 5 * (4 * (3 * (2 * 1)))
= 1202. 斐波那契数列
java
// Fibonacci: 1, 1, 2, 3, 5, 8, 13...
public static int fibonacci(int n) {
if (n == 1 || n == 2) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}3. 递归 vs 循环
java
// 递归实现
public static int sumRecursive(int n) {
if (n == 1) return 1;
return n + sumRecursive(n - 1);
}
// 循环实现
public static int sumIterative(int n) {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return sum;
}对比:
- 递归:代码简洁,但可能栈溢出,性能较低
- 循环:性能好,不会栈溢出,但代码可能复杂
📖 五、可变参数
1. 基本使用
java
public static int sum(int... numbers) {
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}
// 调用
System.out.println(sum(1, 2, 3)); // 6
System.out.println(sum(1, 2, 3, 4, 5)); // 15
System.out.println(sum()); // 02. 可变参数规则
java
// ✅ 可变参数必须是最后一个参数
public static void method(String name, int... scores) { }
// ❌ 错误:可变参数不是最后一个
public static void method(int... scores, String name) { }
// ❌ 错误:只能有一个可变参数
public static void method(int... a, double... b) { }3. 可变参数的本质
java
// 可变参数本质上是数组
public static void print(int... numbers) {
System.out.println(numbers.length); // 数组长度
for (int num : numbers) {
System.out.println(num);
}
}
// 等价于
public static void print(int[] numbers) { }📖 六、return 语句
1. 返回值
java
public static int max(int a, int b) {
if (a > b) {
return a;
}
return b;
}2. 提前返回
java
public static void printPositive(int num) {
if (num <= 0) {
return; // 提前退出
}
System.out.println(num);
}3. 多个 return
java
public static String grade(int score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
if (score >= 60) return "D";
return "F";
}⚠️ 常见陷阱
陷阱1:方法过长
一个方法承担太多职责,会让测试、复用和排查都变困难。方法应围绕一个清晰动作组织。
陷阱2:重载过度
重载方法太多时,调用者很难判断实际调用哪个版本。参数含义接近时可以考虑对象参数。
陷阱3:递归没有出口
递归必须有明确终止条件,否则会导致 StackOverflowError。
陷阱4:可变参数滥用
可变参数适合数量不固定的同类参数,不适合替代结构清晰的参数对象。
🆚 Java vs C 对比
| 特性 | C 函数 | Java 方法 |
|---|---|---|
| 所属关系 | 顶层函数 | 必须属于类或对象 |
| 重载 | 不支持 | 支持按参数列表重载 |
| 参数传递 | 值传递,指针可改外部对象 | 永远值传递,引用值可指向对象 |
| 可变参数 | stdarg.h | T... args |
对 C 程序员来说,Java 方法没有“按引用传参”。对象参数传入的是引用值的副本,因此可以改对象内容,但不能让调用者变量指向新对象。
💡 最佳实践
1. 方法应该短小
java
// ❌ 不好:方法太长
public static void processUser() {
// 100行代码...
}
// ✅ 好:拆分为小方法
public static void processUser() {
validateUser();
saveUser();
sendEmail();
}2. 方法只做一件事
java
// ❌ 不好:做了多件事
public static void processAndSave(User user) {
validate(user);
save(user);
log(user);
}
// ✅ 好:每个方法只做一件事
public static void validateUser(User user) { }
public static void saveUser(User user) { }
public static void logUser(User user) { }3. 避免过深的递归
java
// ❌ 可能栈溢出
factorial(10000);
// ✅ 改用循环📝 练习
完成 练习/Ex05_Methods.java:
- 方法定义和调用
- 参数传递测试
- 方法重载
- 递归实现
- 可变参数
- 综合:数学工具类
🎓 下一步
- 第7课:继承 - extends、super、方法重写