12. 【Java】方法:代码的“积木”

📅 2026/7/15 17:14:52
12. 【Java】方法:代码的“积木”
摘要本文系统讲解了 Java 方法的核心概念从定义、调用、参数传递到重载、递归等高级特性并通过综合示例展示如何用方法实现代码模块化。掌握方法是构建结构化、可维护 Java 程序的基础能有效提升代码复用性和可读性。关键词Java 方法, 方法定义, 方法重载, 递归, 参数传递不知不觉我们已经走过了不少路。从第一篇的 Hello World 到现在的循环和条件判断你写的代码已经从几行增长到了几十行。你有没有发现当代码越来越长时阅读起来会有点吃力就像一篇文章如果只有一个段落读起来会很累。其实写代码和写文章一样需要分段落、分章节。在 Java 里这个“段落”就是方法Method。今天我们来系统地学习方法——它是什么怎么定义怎么调用以及它为什么能让你的代码更清晰、更可复用。学完今天的内容你会发现自己写的代码一下子就有了“结构感”不再是平铺直叙的一堆语句了。1. 什么是方法—— 一个“功能模块”方法就是一段有名字的、可以被重复调用的代码块。你可以把它理解为一个“功能模块”或“小工具”。比如你写了一个“计算两个数的平均值”的功能你可以把它封装成一个方法叫calculateAverage。下次需要求平均值时直接调用这个方法就行了不用再重新写一遍代码。方法的好处代码复用写一次多次使用避免重复劳动。模块化把一个复杂的大问题拆分成若干个小问题每个小问题用一个方法解决。可读性强方法名本身就是一种文档看到printReport()就知道它在打印报表。易于维护如果需要修改某个功能只需要改对应的方法不影响其他部分。2. 方法的基本定义在 Java 中一个方法的定义由修饰符、返回值类型、方法名、参数列表和方法体五部分组成。语法如下修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,...){// 方法体执行的具体代码return返回值;// 如果返回值类型不是 void必须 return}我们拿一个具体例子来看publicstaticintadd(inta,intb){intsumab;returnsum;}拆解public static修饰符稍后解释int返回值类型——这个方法会返回一个整数add方法名——我们给它起的名字(int a, int b)参数列表——它需要两个整数作为输入{ ... }方法体——具体的执行逻辑return sum;返回计算结果返回值类型如果方法有返回值必须声明返回类型如int、double、String等并且在方法体内用return语句返回一个匹配类型的值。如果方法没有返回值只执行一些操作不返回数据返回类型写void。此时return可以省略或者写return;来提前结束方法。publicstaticvoidprintHello(){System.out.println(Hello, World!);// 没有 return因为 void}参数列表参数列表可以为空即无参方法也可以有多个参数用逗号分隔。每个参数必须声明类型和名称在方法内部可以像使用局部变量一样使用它们。3. 方法的调用定义好方法后怎么让它执行呢通过方法调用——在代码中写上方法名和实参实际参数。intresultadd(5,3);// 调用 add 方法传入 5 和 3返回值赋给 resultSystem.out.println(result);// 输出 8调用时实参的类型、数量和顺序必须与定义时的形参形式参数一致。如果方法没有返回值void调用时直接写方法名和参数即可不能用在赋值语句中。printHello();// 调用无参无返回值方法4. 参数传递Java 是“值传递”这是一个非常重要的概念。Java 在调用方法时所有参数都是按值传递的pass-by-value。对于基本类型int、double 等传递的是值的副本。方法内部修改参数的值不会影响外部的原始变量。publicstaticvoidchangeInt(intx){x100;}publicstaticvoidmain(String[]args){intnum10;changeInt(num);System.out.println(num);// 输出 10不是 100}对于引用类型如数组、String、对象传递的是引用的副本也就是对象地址的拷贝。方法内通过引用修改对象的属性会影响外部对象但如果把引用本身指向新的对象外部不会受影响。publicstaticvoidchangeArray(int[]arr){arr[0]999;// 修改数组元素会影响外部arrnewint[]{1,2,3};// 重新赋值引用不影响外部}publicstaticvoidmain(String[]args){int[]myArr{10,20};changeArray(myArr);System.out.println(myArr[0]);// 输出 999因为第一个修改生效了}关于引用传递我们会在面向对象阶段深入讲解现在你只需记住Java 永远只有值传递只是引用类型的“值”恰好是对象的地址。5. 方法重载Overloading—— 同名的“多面手”有时候我们需要实现多个功能类似但参数不同的方法比如求最大值的函数可能有时比较两个整数有时比较三个整数有时比较两个小数。如果给它们起不同的名字max2、max3、maxDouble调用时得记很多名字很麻烦。Java 允许我们定义多个同名方法只要它们的参数列表不同参数的个数、类型或顺序不同。这叫做方法重载Overloading。publicstaticintmax(inta,intb){return(ab)?a:b;}publicstaticintmax(inta,intb,intc){returnmax(max(a,b),c);// 调用重载的 two-arg 版本}publicstaticdoublemax(doublea,doubleb){return(ab)?a:b;}调用时编译器会根据你传入的实参类型和数量自动匹配最合适的方法。System.out.println(max(5,3));// 调用 int int 版本System.out.println(max(5,3,7));// 调用 int int int 版本System.out.println(max(2.5,1.8));// 调用 double double 版本⚠️注意重载与返回值类型无关。如果两个方法参数列表完全相同只是返回类型不同编译器会报错它们不是重载而是重复定义。6. 方法的递归 —— 方法调用自身一个方法在内部调用自己叫做递归Recursion。递归是解决某些问题如树的遍历、阶乘、斐波那契数列等非常优雅的方式。递归必须满足两个条件终止条件Base Case什么时候停止递归不再调用自身。递归步骤每次调用都向终止条件靠近。经典的阶乘计算// n! n * (n-1) * ... * 1publicstaticintfactorial(intn){if(n1){return1;// 终止条件0! 1, 1! 1}returnn*factorial(n-1);// 递归调用}调用factorial(5)的执行过程factorial(5)→ 5 *factorial(4)factorial(4)→ 4 *factorial(3)factorial(3)→ 3 *factorial(2)factorial(2)→ 2 *factorial(1)factorial(1)→ 返回 1然后逐层返回得到 54321 120递归虽然简洁但每次调用都会占用栈内存如果递归层数太深可能导致StackOverflowError。所以对于循环能解决的问题通常优先使用循环递归更适合树结构等本身具有递归性质的问题。7.static关键字 —— 类方法 vs 实例方法你可能注意到了我们前面定义的方法都加了static。static是修饰符意思是“静态的”它表示这个方法属于类本身而不属于某个特定的对象。静态方法类方法用static修饰可以在没有创建对象的情况下直接通过类名调用比如Math.sqrt()。在main方法中调用同一个类里的静态方法可以直接写方法名。非静态方法实例方法没有static必须通过对象来调用new 类名().方法名()。我们会在面向对象部分详细讲解。现阶段我们暂时把所有方法都定义为static这样便于在main中直接调用不需要绕弯子。等你理解了对象的概念后再区分静态和非静态的用法。8. 方法的命名规范方法和变量一样使用驼峰命名法camelCase首字母小写后续单词首字母大写。动词开头表达“做什么”calculateTotal、findMax、printReport。如果是获取某个属性通常以get开头getName、getAge。如果是判断条件以is或has开头isValid、hasNext。良好的命名让代码像一句英文句子一样流畅易读。9. 综合示例 —— 计算器功能的模块化我们来把之前“命令行计算器”的想法用方法重写一下将每个运算封装成独立的方法。packagecom.example.demo;importjava.util.Scanner;/** * 演示方法的定义、调用、重载和参数传递 * 实现一个简单的命令行计算器 */publicclassCalculator{// ---- 加法 ----publicstaticintadd(inta,intb){returnab;}// ---- 减法 ----publicstaticintsubtract(inta,intb){returna-b;}// ---- 乘法 ----publicstaticintmultiply(inta,intb){returna*b;}// ---- 除法 ----publicstaticdoubledivide(inta,intb){if(b0){System.out.println(错误除数不能为0);returnDouble.NaN;// 返回一个特殊值表示错误}return(double)a/b;// 强制转为 double 得到小数结果}// ---- 重载计算平方int ----publicstaticintsquare(inta){returna*a;}// ---- 重载计算平方double ----publicstaticdoublesquare(doublea){returna*a;}// ---- 主入口 ----publicstaticvoidmain(String[]args){ScannerscannernewScanner(System.in);System.out.println( 命令行计算器 );System.out.print(请输入第一个整数);intnum1scanner.nextInt();System.out.print(请输入第二个整数);intnum2scanner.nextInt();// 调用各种方法System.out.println(num1 num2 add(num1,num2));System.out.println(num1 - num2 subtract(num1,num2));System.out.println(num1 * num2 multiply(num1,num2));doubledivResultdivide(num1,num2);if(!Double.isNaN(divResult)){System.out.println(num1 / num2 divResult);}// 演示重载System.out.println(num1 的平方int square(num1));doubled3.5;System.out.println(d 的平方double square(d));scanner.close();}}运行这个程序你会发现所有的运算都通过方法调用来完成主方法变得非常简洁只负责控制流程和用户交互具体的计算逻辑都封装在各自的方法里。10. 方法设计的最佳实践单一职责一个方法只做一件事。比如不要在一个方法里既计算又打印而是让一个方法计算并返回结果另一个方法负责打印。命名要见名知意好的方法名本身就是注释。参数不要太多如果超过 3-4 个参数考虑把参数封装成一个对象。避免副作用方法最好只根据输入参数返回结果不要修改外部的全局状态除非有明确理由。验证参数合法性在方法开始处检查参数是否满足要求如除数不为零、数组不为空等提前发现问题。今天的总结今天我们系统学习了方法方法的定义修饰符、返回值类型、方法名、参数列表、方法体。方法的调用通过方法名和实参。参数传递值传递基本类型传副本引用类型传引用副本。方法重载同名不同参让接口更统一。递归方法调用自身需有终止条件。static的作用让方法可以直接在main中调用。方法的命名和设计原则。方法是你编程路上的“积木块”掌握好它你就能搭建出结构清晰、易于维护的代码大厦。动手试试写一个方法isEven(int n)判断一个整数是否为偶数返回boolean。然后在main中调用并测试。写一个重载方法printArray一个版本接收int[]另一个接收String[]分别打印数组的所有元素。写一个递归方法fibonacci(int n)返回斐波那契数列的第 n 项第 1 项1第 2 项1第 3 项2…。然后用循环版本对比效率递归版本在 n 较大时会很慢可以体会一下。把第 6 篇的“个人名片生成器”重构将收集信息和打印名片分别封装成方法让main只负责整体流程。我们下一篇将讲数组一种可以存放多个同类型数据的容器它是我们迈向集合框架的第一步。我们下一篇见。 获取本系列示例代码请访问 GitCode。