Skip to content

Chapter 06 · 方法(函数)

把"重复的事"封装成一个动作,按下按钮就执行 — 这就是方法。


🎯 本章目标

  • 学会定义方法、调用方法、理解 方法签名
  • 理解 参数传递:值传递的真相
  • 掌握 方法重载(同名不同参)
  • 学会写 递归 方法
  • 知道 可变参数 varargs 怎么用

1. 方法是什么?洗衣机的故事

家里有一台洗衣机:

  • 你不需要知道里面齿轮怎么转、水怎么注、电怎么走
  • 你只要:"放衣服 → 按按钮 → 等结果"
  • 按钮(输入) → 黑盒 → 干净衣服(输出)

这就是方法:接受一些输入,做点事情,可能返回一个结果。

java
public static int max(int a, int b) {  // 输入: a, b
    if (a > b) return a;                // 处理
    return b;                            // 输出
}

2. 方法的解剖图

方法结构

java
public  static  int  max(int a, int b) {
  ↑       ↑     ↑    ↑    ↑
访问修饰  static 返回  方法名 参数列表
                类型

五个组成部分

部分例子作用
访问修饰符public谁能调用(后面 OOP 章节细讲)
静态修饰static是否需要先 new 对象(先不管,main 里默认写 static)
返回类型int结果是什么类型;没有返回写 void
方法名max小驼峰命名
参数列表(int a, int b)输入,可以为空

方法签名 = 方法名 + 参数列表

JVM 通过 方法签名 区分两个方法。返回类型不参与签名

java
int add(int a, int b)       // 签名: add(int, int)
double add(int a, int b)    // ❌ 编译错误:与上面签名相同

3. 调用方法

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

    public static void greet(String name) {
        System.out.println("Hello, " + name);
    }

    public static void main(String[] args) {
        int m = max(3, 5);      // 接收返回值
        System.out.println(m);

        greet("张三");           // 不接收返回值(void)
    }
}

4. 参数传递的真相:值传递

🎯 Java 只有"值传递" —— 这是面试高频考点。

基本类型:传值的副本

java
static void change(int n) {
    n = 100;
}
public static void main(String[] args) {
    int x = 5;
    change(x);
    System.out.println(x);  // 还是 5!
}

引用类型:传"引用的副本"

java
static void modify(int[] arr) {
    arr[0] = 999;          // ✅ 改原数组
    arr = new int[]{1};    // ❌ 这只是改了局部副本
}
public static void main(String[] args) {
    int[] a = {1, 2, 3};
    modify(a);
    System.out.println(a[0]);     // 999
    System.out.println(a.length); // 3(引用没变)
}

参数传递机制

💡 正确理解:传的是 引用变量本身的值(即对象的地址)。能改对象内容,但改不了"指向哪个对象"。


5. 方法重载(Overload)

同一个类里,方法名相同但参数列表不同,叫重载。

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

调用时编译器根据实参类型 自动选择

java
MathUtil.max(3, 5);          // 调第 1 个
MathUtil.max(3.0, 5.0);      // 调第 2 个
MathUtil.max(3, 5, 8);       // 调第 3 个

重载的规则

区分点是否区分重载
方法名✅ 必须相同
参数个数✅ 不同算重载
参数类型✅ 不同算重载
参数顺序✅ 不同算重载(如 (int,String) vs (String,int)
返回类型❌ 不区分
参数名❌ 不区分

经典案例:System.out.println

JDK 重载了 10 个 println:传啥都能打印。


6. 递归:自己调用自己

🎯 递归 = 解决问题的方法 = 解决子问题的同样方法

经典例子:阶乘 n!

数学定义:

n! = n × (n-1)!
0! = 1

代码:

java
public static int factorial(int n) {
    if (n == 0) return 1;        // 终止条件(必须有!)
    return n * factorial(n - 1); // 递归调用
}

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 * factorial(0)
// = 5 * 4 * 3 * 2 * 1 * 1 = 120

递归调用栈

递归三要素

  1. 终止条件:什么时候停?(如 n == 0
  2. 递归调用:调用自己处理子问题(如 factorial(n-1)
  3. 每次缩小问题规模:让 n 越来越小,否则栈溢出!

经典面试题:斐波那契

java
// 递归版(简洁但慢,O(2^n))
public static long fib(int n) {
    if (n <= 1) return n;
    return fib(n - 1) + fib(n - 2);
}

// 循环版(快得多,O(n))
public static long fibIter(int n) {
    if (n <= 1) return n;
    long a = 0, b = 1;
    for (int i = 2; i <= n; i++) {
        long c = a + b;
        a = b;
        b = c;
    }
    return b;
}

⚠️ 递归虽然简洁,栈空间有限,太深会 StackOverflowError。生产代码优先用循环。


7. 可变参数 varargs

需要传任意多个同类型参数?用 ...

java
public static int sum(int... nums) {   // nums 实际是 int[]
    int s = 0;
    for (int n : nums) s += n;
    return s;
}

sum();              // 0
sum(1);             // 1
sum(1, 2, 3);       // 6
sum(1, 2, 3, 4, 5); // 15

💡 限制:可变参数必须放在 参数列表的最后

java
void log(String level, Object... args) { ... }   // ✅
void log(Object... args, String level) { ... }   // ❌

8. 实战练习

文件内容
MethodBasics.java方法定义与调用
OverloadDemo.java方法重载
RecursionDemo.java阶乘、斐波那契、汉诺塔
VarargsDemo.java可变参数
ParamPassingDemo.java参数传递实验

9. 浏览器演示

打开 demo.html

  • 方法调用栈可视化
  • 递归过程动画
  • 重载选择演示

10. 面试可能会问什么?

Q1: Java 是值传递还是引用传递?

只有值传递

  • 基本类型:传值的副本
  • 引用类型:传 引用值的副本(即对象地址的副本)

虽然能改对象内容,但 不能改变实参指向,这正是值传递的特征。

Q2: 方法重载和重写的区别?

对比重载 Overload重写 Override
位置同一个类父类与子类
方法名必须相同必须相同
参数列表必须不同必须相同
返回类型无要求必须相同(或子类型)
何时绑定编译时(静态分派)运行时(动态分派)

Q3: 递归有什么风险?

  1. 栈溢出(StackOverflowError):递归过深
  2. 重复计算:如朴素的斐波那契 fib(40) 巨慢
  3. 可读性 vs 性能:能用循环就用循环

Q4: static 方法和实例方法的区别?

  • static 方法:属于类,不需要创建对象
  • 实例方法:必须 new 一个对象才能调
java
Math.max(3, 5);            // static,直接调
"hello".length();          // 实例方法,需要先有对象

Q5: 方法可以有同名的形参吗?

java
public void foo(int a, int a) { ... }   // ❌ 编译错误

不可以。形参名在方法内必须唯一。

Q6: Java 没有"默认参数",怎么实现?

:用 方法重载 模拟:

java
public void send(String msg) { send(msg, "INFO"); }
public void send(String msg, String level) { ... }

🎁 本章小结

✅ 方法 = 输入参数 + 处理逻辑 + 输出返回值
✅ 方法签名 = 方法名 + 参数列表(不含返回类型)
✅ Java 只有值传递(包括"引用值的副本")
✅ 重载:同名不同参;重写:父子覆盖
✅ 递归三要素:终止条件、递归调用、缩小规模
✅ 可变参数 ... 本质是数组,必须放最后

🔗 导航