Skip to content
第 10 / 250 章后端⏱ 10 分钟阅读

第 10 章:方法定义、调用与重载

学习目标

  • 掌握方法的定义、调用、参数传递
  • 理解方法重载(Overload)
  • 学会递归
  • 理解"值传递"的本质

一、什么是方法?

方法(Method) = 一段有名字的、可重复调用的代码块。

为什么要用方法?

二、方法定义

2.1 基本语法

java
修饰符 返回类型 方法名(参数列表) {
    方法体
    return 返回值;  // 如果返回类型不是 void
}

2.2 简单示例

java
/**
 * 计算两个整数的和
 *
 * @param a 第一个加数
 * @param b 第二个加数
 * @return 两数之和
 */
public static int add(int a, int b) {  // ① 修饰符 + 返回类型 + 方法名 + 参数
    return a + b;                       // ② 返回值
}

2.3 方法签名

方法签名 = 方法名 + 参数列表(不包括返回类型和修饰符)

java
// 以下三个方法是不同的(方法签名不同)
public void print(int x) { }
public void print(String s) { }
public void print(int x, int y) { }

方法签名是区分不同方法的唯一依据,返回类型不影响签名

2.4 无返回值:void

java
public static void printHello() {  // ① void 表示无返回值
    System.out.println("Hello");
    // 没有 return 或 return 后无值
}

三、方法调用

3.1 静态方法调用

java
public class Calculator {
    public static int add(int a, int b) {  // ① static 方法
        return a + b;
    }

    public static void main(String[] args) {
        int sum = add(3, 5);              // ② 同类直接调用
        System.out.println("sum = " + sum);
    }
}

3.2 不同类的静态方法调用

java
public class MathUtils {
    public static int max(int a, int b) {
        return a > b ? a : b;
    }
}

public class Test {
    public static void main(String[] args) {
        int m = MathUtils.max(10, 20);   // ① 通过类名调用
        System.out.println(m);             // 20
    }
}

3.3 实例方法调用(第 14 章详解)

java
public class UserService {
    public String greet(String name) {    // ① 实例方法(无 static)
        return "Hello, " + name;
    }
}

public class Test {
    public static void main(String[] args) {
        UserService service = new UserService();  // ① 创建对象
        String result = service.greet("张三");     // ② 通过对象调用
        System.out.println(result);
    }
}

四、方法参数

4.1 形参 vs 实参

java
public static int add(int a, int b) {  // ① a, b 是形参(形式参数)
    return a + b;
}

public static void main(String[] args) {
    int x = 3, y = 5;
    int result = add(x, y);              // ② x, y 是实参(实际参数)
}

4.2 Java 只有值传递(重要!)

java
public static void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

public static void main(String[] args) {
    int x = 10, y = 20;
    swap(x, y);
    System.out.println("x = " + x + ", y = " + y);  // x = 10, y = 20(没变!)
}

为什么没变?

Java 把实参的复制一份传给形参,方法内的修改只影响副本:

4.3 引用类型也是值传递

java
public static void modify(User user) {  // ① 接收引用副本
    user.setName("李四");                // ② 修改对象内容生效
    user = null;                         // ③ 修改引用副本,不影响外面
}

User u = new User("张三");
modify(u);
System.out.println(u.getName());  // 李四(对象被修改了)
System.out.println(u);            // 非 null(引用没被改)

关键结论:Java 永远是值传递,但引用类型的"值"是地址,所以可以修改对象内容。

五、方法重载(Overload)

同一个类中,方法名相同但参数列表不同

java
public class Calculator {

    // 两个 int 相加
    public int add(int a, int b) {
        return a + b;
    }

    // 三个 int 相加(参数个数不同)
    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // 两个 double 相加(参数类型不同)
    public double add(double a, double b) {
        return a + b;
    }

    // 一个 int,一个 String(顺序不同也算重载)
    public String add(int a, String b) {
        return a + b;
    }
}

5.1 重载的判定规则

方法签名不同 = 方法重载。返回值类型、修饰符不影响重载:

java
// ✅ 合法重载(参数列表不同)
public void print(int x) { }
public void print(String s) { }
public void print(int x, int y) { }

// ❌ 不是重载(参数列表相同)
// public int print(int x) { return x; }      // 与上面第一个 print(int x) 冲突
// public void print(int x) { }                 // 编译报错

5.2 编译器如何选择重载方法?

java
public static void main(String[] args) {
    Calculator calc = new Calculator();
    calc.add(1, 2);          // 调用 add(int, int)
    calc.add(1.0, 2.0);      // 调用 add(double, double)
    calc.add(1, 2, 3);       // 调用 add(int, int, int)
}

匹配优先级:精确匹配 > 自动类型转换 > 装箱 > 可变参数

java
public void test(int x) { System.out.println("int"); }
public void test(long x) { System.out.println("long"); }
public void test(Integer x) { System.out.println("Integer"); }

test(10);  // 调用 test(int),因为是精确匹配

六、递归

方法调用自身

java
/**
 * 计算阶乘 n! = n × (n-1) × ... × 2 × 1
 */
public static int factorial(int n) {
    if (n <= 1) {                       // ① 递归终止条件(必须)
        return 1;
    }
    return n * factorial(n - 1);        // ② 递归调用
}

factorial(5);  // 5! = 5×4×3×2×1 = 120

6.1 递归的两个要素

6.2 斐波那契数列

java
/**
 * 斐波那契:F(1)=1, F(2)=1, F(n)=F(n-1)+F(n-2)
 */
public static int fib(int n) {
    if (n <= 2) return 1;
    return fib(n - 1) + fib(n - 2);
}

6.3 递归的缺点与优化

java
// ❌ 效率低:fib(40) 会有指数级调用
fib(40);  // 几秒都算不完

// ✅ 优化:用循环
public static int fibIterative(int n) {
    if (n <= 2) return 1;
    int a = 1, b = 1, c;
    for (int i = 3; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

企业级原则:能用循环就不要用递归(除非逻辑天然适合递归,如树遍历)。

七、方法设计原则

7.1 单一职责

java
// ❌ 反例:一个方法做多件事
public void processUser() {
    validate();
    saveToDb();
    sendEmail();
    log();
}

// ✅ 正例:拆分
public void createUser(User user) {
    validateUser(user);
    saveUser(user);
    notifyUserCreated(user);
}

7.2 短小精悍

java
// ❌ 反例:方法 200 行
public void process() {
    // 100 行
}

// ✅ 正例:拆分为多个小方法
public void process() {
    step1();
    step2();
    step3();
}

阿里规约:单个方法不超过 80 行。

7.3 命名规范

  • 方法名 lowerCamelCase
  • 动词或动词短语:saveUserfindByIdcalculateTotal
  • 布尔方法:is / has / can 前缀:isActivehasPermission

八、完整代码示例

java
/**
 * 方法综合示例
 *
 * 配套文档:第 10 章 方法定义、调用与重载
 * 运行方式:javac MethodDemo.java && java MethodDemo
 */
public class MethodDemo {

    // ============ 1. 方法重载 ============
    public static int add(int a, int b) {
        return a + b;
    }

    public static int add(int a, int b, int c) {     // ① 参数个数不同
        return a + b + c;
    }

    public static double add(double a, double b) {   // ② 参数类型不同
        return a + b;
    }

    // ============ 2. 递归:阶乘 ============
    public static long factorial(int n) {
        if (n <= 1) return 1;
        return n * factorial(n - 1);
    }

    // ============ 3. 递归:斐波那契 ============
    public static long fib(int n) {
        if (n <= 2) return 1;
        return fib(n - 1) + fib(n - 2);
    }

    // ============ 4. 数组求和 ============
    public static int sum(int[] arr) {
        int total = 0;
        for (int num : arr) {
            total += num;
        }
        return total;
    }

    // ============ 5. 可变参数(第 11 章详解)============
    public static int sumVarargs(int... numbers) {
        int total = 0;
        for (int num : numbers) {
            total += num;
        }
        return total;
    }

    // ============ 6. 主方法 ============
    public static void main(String[] args) {
        // 调用重载方法
        System.out.println("=== 方法重载 ===");
        System.out.println("add(1, 2) = " + add(1, 2));
        System.out.println("add(1, 2, 3) = " + add(1, 2, 3));
        System.out.println("add(1.5, 2.5) = " + add(1.5, 2.5));

        // 递归
        System.out.println("\n=== 递归 ===");
        System.out.println("5! = " + factorial(5));   // 120
        System.out.println("fib(10) = " + fib(10));   // 55

        // 数组求和
        System.out.println("\n=== 数组求和 ===");
        int[] scores = {85, 92, 78, 95, 88};
        System.out.println("总分 = " + sum(scores));   // 438

        // 可变参数
        System.out.println("\n=== 可变参数 ===");
        System.out.println("sumVarargs(1, 2, 3) = " + sumVarargs(1, 2, 3));
        System.out.println("sumVarargs(1, 2, 3, 4, 5) = " + sumVarargs(1, 2, 3, 4, 5));

        // 值传递演示
        System.out.println("\n=== 值传递 ===");
        int x = 10, y = 20;
        swap(x, y);
        System.out.println("x = " + x + ", y = " + y);  // 10, 20(没变)
    }

    public static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
        // 这里 a, b 交换了,但外面的 x, y 不受影响
    }
}

运行结果

=== 方法重载 ===
add(1, 2) = 3
add(1, 2, 3) = 6
add(1.5, 2.5) = 4.0

=== 递归 ===
5! = 120
fib(10) = 55

=== 数组求和 ===
总分 = 438

=== 可变参数 ===
sumVarargs(1, 2, 3) = 6
sumVarargs(1, 2, 3, 4, 5) = 15

=== 值传递 ===
x = 10, y = 20

九、常见面试题

Q1: 方法重载 vs 方法重写的区别?

重载(Overload)重写(Override)
发生位置同一个类子类继承父类
方法签名不同相同
返回类型可以不同必须相同或协变
修饰符无限制不能更严格
发生时期编译期运行期

Q2: Java 真的是值传递吗?

是的。引用类型的"值"是引用地址,所以可以通过引用修改对象,但不能修改外部引用本身。

Q3: 递归 vs 循环?

递归循环
代码简洁
性能❌(栈开销)
内存❌(栈溢出风险)
适用树、图、分治大多数场景

十、本章小结

要点关键
方法的本质一段有名字、可重复调用的代码块
方法签名方法名 + 参数列表(不含返回类型)
重载规则同类同名,参数列表不同
值传递Java 永远是值传递,引用类型传的是地址副本
递归终止条件 + 递归公式,但能用循环就别用递归
设计原则单一职责、短小精悍、命名规范

动手练习

练习 1:基础题

写一个 MathUtils 类,实现:

java
public static int max(int a, int b)        // 两数最大值
public static int max(int a, int b, int c) // 三数最大值(重载)
public static double max(double a, double b) // 重载

练习 2:进阶题

用递归实现整数反转

java
public static int reverse(int n)
// reverse(123) = 321
// reverse(-123) = -321
// reverse(1200) = 21

练习 3:挑战题

不用任何临时变量,交换两个 int 的值(提示:用加减法或异或,第 7 章学过)。

推荐阅读


下一章第 11 章:可变参数与命令行参数

本站基于 VitePress 构建 · 由 Codebook 团队维护