Java6:Java方法详解:让代码告别重复,拥抱复用

📅 2026/7/7 19:19:51 👁️ 阅读次数 📝 编程学习
Java6:Java方法详解:让代码告别重复,拥抱复用

目录

写在前面

一、方法是什么?用生活场景理解

二、方法的定义与使用

2.1 基本语法

2.2 方法定义的注意事项

三、方法的调用

方法的执行过程

实战:用方法简化阶乘求和

四、形参和实参

4.1 基本概念

4.2 一个经典问题:为什么交换不了?

五、方法重载

5.1 为什么需要重载?

5.2 重载的概念

5.3 重载的规则

5.4 方法签名

六、递归:方法调用自身

6.1 递归的思想

6.2 递归的两个必要条件

6.3 代码实现:递归求阶乘

6.4 递归的执行过程

6.5 递归练习

七、总结


写在前面

经过前面几篇文章的学习,我们已经掌握了变量、运算符、流程控制和输入输出。但不知你有没有发现一个问题:当我们的程序越来越复杂时,代码变得越来越臃肿,同样的逻辑反复出现,修改一处就要改动多处。

比如我们要计算1!、2!、3!……5!,如果不使用方法,就得把阶乘的逻辑重复写五遍。这不仅浪费时间,还容易出错。

今天这篇文章,我们来学习Java中最重要的代码组织方式——方法。掌握了方法,你的代码将从"流水账"升级为"模块化"。


一、方法是什么?用生活场景理解

想象一下你在家里制作咖啡。整个过程大致是这样的:

  1. 准备材料:咖啡豆、水、糖、牛奶

  2. 磨咖啡豆:将咖啡豆磨成粉

  3. 煮水:将水煮沸

  4. 冲泡:将热水倒入咖啡粉中,浸泡几分钟

  5. 添加配料:根据个人喜好添加糖或牛奶

  6. 享用:倒入杯中,品尝美味的咖啡

每次想喝咖啡都要重复这一整套流程,是不是很麻烦?但如果有一个"一键制作咖啡"的机器呢?你只需要放入材料(参数),按下按钮(调用),稍等片刻就能得到一杯咖啡(返回值)。

Java中的方法(method)就是这个道理。​ 它是一段代码的集合,用来完成某个特定的功能。有了方法,我们就不用每次都从头写一遍相同的逻辑,直接"调用"就行了。


二、方法的定义与使用

2.1 基本语法

修饰符 返回值类型 方法名称(参数类型 形参...) { 方法体代码; [return 返回值]; }

来看一个具体的例子——判断闰年:

public static boolean isLeapYear(int year) { if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { return true; } else { return false; } }

再看一个加法方法:

public static int add(int x, int y) { return x + y; }

2.2 方法定义的注意事项

  1. 修饰符:现阶段统一使用public static固定搭配

  2. 返回值类型:有返回值时,类型必须与返回的实体一致;没有返回值时,写成void

  3. 方法名:采用小驼峰命名法,如isLeapYearcalculateSum

  4. 参数列表:没有参数就留空,多个参数用逗号隔开

  5. 方法体:方法内部要执行的语句

  6. 方法必须写在类中,不能嵌套定义

  7. Java中没有方法声明一说,定义即声明


三、方法的调用

方法写好了,但它不会自动执行。只有被调用时才会运行。

public class MethodDemo { public static void main(String[] args) { // 调用方法 int result = add(10, 20); System.out.println("10 + 20 = " + result); boolean flag = isLeapYear(2024); System.out.println("2024年是闰年吗?" + flag); } public static int add(int x, int y) { return x + y; } public static boolean isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; } }

调用规则总结:

  • 定义方法时,代码不会执行,只有调用时才执行

  • 调用时使用方法名(参数)的形式

  • 如果方法有返回值,需要用变量接收

方法的执行过程

当方法被调用时,程序会跳转到方法内部执行,执行完毕后回到调用处继续往下走。

public static void main(String[] args) { System.out.println("第一次调用之前"); int ret = add(10, 20); System.out.println("第一次调用之后,ret=" + ret); System.out.println("第二次调用之前"); ret = add(30, 50); System.out.println("第二次调用之后,ret=" + ret); } public static int add(int x, int y) { System.out.println("调用方法中,x=" + x + " y=" + y); return x + y; }

执行结果:

第一次调用之前 调用方法中,x=10 y=20 第一次调用之后,ret=30 第二次调用之前 调用方法中,x=30 y=50 第二次调用之后,ret=80

可以看到,同一个方法可以被多次调用,每次传入不同的参数,得到不同的结果。

实战:用方法简化阶乘求和

回想一下我们在循环章节写的1!+2!+...+5!的代码,当时用了嵌套循环。现在有了方法,代码会清爽很多:

public static void main(String[] args) { int sum = 0; for (int i = 1; i <= 5; i++) { sum += factorial(i); } System.out.println("sum = " + sum); } public static int factorial(int n) { int result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; }

执行结果:

sum = 153

对比之前的嵌套循环,用方法把阶乘的计算封装起来,主逻辑变得清晰易懂。这就是方法的核心价值——模块化组织代码


四、形参和实参

4.1 基本概念

  • 形参:方法定义时括号里的参数,相当于数学函数中的自变量

  • 实参:方法调用时实际传入的值

public static int add(int x, int y) { // x和y是形参 return x + y; } // 调用时 int result = add(10, 20); // 10和20是实参

4.2 一个经典问题:为什么交换不了?

来看这段代码:

public static void main(String[] args) { int a = 10; int b = 20; System.out.println("交换前:a=" + a + " b=" + b); swap(a, b); System.out.println("交换后:a=" + a + " b=" + b); } public static void swap(int x, int y) { int temp = x; x = y; y = temp; }

运行结果:

交换前:a=10 b=20 交换后:a=10 b=20

明明在swap方法里交换了,为什么a和b的值没变?

原因分析:​ 实参a和b是main方法中的变量,存储在main方法的栈空间中;形参x和y是swap方法中的变量,存储在swap方法的栈空间中。两者是完全独立的实体。调用swap时,只是把a和b的值拷贝了一份给x和y,对x和y的操作自然不会影响到a和b。

一句话总结:实参的值永远都是拷贝给形参,形参和实参是两个不同的实体。


五、方法重载

5.1 为什么需要重载?

假设我们想写一个加法方法,既要能加整数,又要能加小数。按照之前的思路,可能需要两个不同的方法名:

public static int addInt(int x, int y) { ... } public static double addDouble(double x, double y) { ... }

但取名本身就是一件麻烦事。能不能都用同一个名字呢?

5.2 重载的概念

方法重载:在同一个类中,多个方法名字相同,但参数列表不同。

public static int add(int x, int y) { return x + y; } public static double add(double x, double y) { return x + y; } public static int add(int x, int y, int z) { return x + y + z; }

调用时,编译器会根据实参的类型和个数,自动匹配最合适的方法:

int r1 = add(10, 20); // 调用第一个 double r2 = add(10.5, 20.5); // 调用第二个 int r3 = add(10, 20, 30); // 调用第三个

5.3 重载的规则

  1. 方法名必须相同

  2. 参数列表必须不同(个数不同、类型不同、类型顺序不同,满足其一即可)

  3. 与返回值类型无关

以下两种情况不构成重载:

// 错误:只有返回值不同,参数列表相同 public static int add(int x, int y) { ... } public static double add(int x, int y) { ... } // 编译报错 // 错误:只有形参名不同 public static int add(int x, int y) { ... } public static int add(int a, int b) { ... } // 编译报错

5.4 方法签名

你可能好奇:既然方法名相同,编译器是怎么区分它们的?

答案在于方法签名。经过编译后,方法的名字不再是简单的"add",而是"方法全路径名 + 参数列表"的组合。比如:

  • add(int, int)→ 方法签名为add(II)I

  • add(double, double)→ 方法签名为add(DD)D

这就是为什么参数列表不同的同名方法可以共存的原因——它们在底层有完全不同的标识。


六、递归:方法调用自身

6.1 递归的思想

从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的是:"从前有座山,山上有座庙……"

这个故事的特征是:自身中包含了自己。这种思想在编程中叫做递归

递归的核心是:把一个大问题分解成与原问题解法相同的子问题,直到子问题简单到可以直接求解。

6.2 递归的两个必要条件

  1. 递归公式:将原问题拆分成子问题的规则

  2. 递归出口:递归结束的条件

以阶乘为例:

  • 递归公式:N! = N * (N-1)!

  • 递归出口:1! = 1

6.3 代码实现:递归求阶乘

public static int factorial(int n) { if (n == 1) { return 1; // 递归出口 } return n * factorial(n - 1); // 递归公式 }

6.4 递归的执行过程

factorial(3)为例,执行过程如下:

factorial(3) 开始 → factorial(2) 开始 → factorial(1) 开始 → factorial(1) 结束,返回 1 → factorial(2) 结束,返回 2 × 1 = 2 → factorial(3) 结束,返回 3 × 2 = 6

每次调用方法时,Java会在栈中分配一块空间(称为栈帧),记录当前调用的参数和返回位置。递归结束时,栈帧从顶到底依次弹出,最终得到结果。

6.5 递归练习

练习1:按顺序打印数字的每一位

public static void printDigits(int num) { if (num > 9) { printDigits(num / 10); } System.out.println(num % 10); } // 输入1234,输出1 2 3 4

练习2:递归求1到N的和

public static int sum(int num) { if (num == 1) { return 1; } return num + sum(num - 1); }

练习3:求斐波那契数列第N项

斐波那契数列的定义:F(1)=1, F(2)=1, F(N)=F(N-1)+F(N-2)

public static int fib(int n) { if (n == 1 || n == 2) { return 1; } return fib(n - 1) + fib(n - 2); }

注意:​ 递归求斐波那契的效率很低,因为存在大量重复计算。比如计算fib(40)时,fib(3)会被重复计算上千万次。实际开发中,斐波那契更适合用循环来实现。


七、总结

今天我们学习了Java方法的全部核心知识:

  1. 方法的定义:修饰符 + 返回值类型 + 方法名 + 参数列表 + 方法体

  2. 方法的调用:定义时不执行,调用时才执行,可多次调用

  3. 形参和实参:实参的值拷贝给形参,两者是独立的实体

  4. 方法重载:同名不同参,编译器自动匹配

  5. 递归:方法调用自身,必须有递归公式和递归出口

方法让代码从"线性执行"升级为"模块化组织",是Java编程的重要里程碑。接下来,我们将马上进入Java的面向对象世界——类和对象,敬请期待!