方法、参数、返回值与方法重载 | JavaSE

方法、参数、返回值与方法重载

一、学习目标

完成本章后,你应该能够:

  • 能够解释 Java 方法(Method)的作用以及为什么需要方法。
  • 能够从零定义并调用无参数、有参数、无返回值和有返回值方法。
  • 能够区分形式参数(Formal Parameter)与实际参数(Argument)。
  • 能够正确使用 return 返回结果或提前结束方法。
  • 能够理解方法调用过程中参数变量的基本工作方式。
  • 能够解释 Java 参数传递的值传递(Pass by Value)语义。
  • 能够判断多个方法是否构成方法重载(Overloading)。
  • 能够根据方法名和参数列表判断编译器会选择哪个重载方法。
  • 能够把过长的 main 方法拆分成职责清晰、可以复用的小方法。

二、核心知识

2.1 什么是方法

方法(Method)是一段:

为完成某个明确功能而组织起来、可以被调用执行的代码。

例如一个程序需要多次求两个整数的最大值。

如果每次都写:

int max = a > b ? a : b;

程序中会出现大量重复逻辑。

我们可以把这个功能封装为方法:

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

以后需要时直接调用:

int result = max(10, 20);

因此可以把方法理解为:

输入数据
   ↓
方法
   ↓
处理逻辑
   ↓
输出结果

方法帮助我们解决几个核心问题:

代码重复
职责混乱
main 方法过长
代码难以测试
代码难以复用
代码难以维护

2.2 为什么需要方法

假设现在需要:

  1. 计算 10 和 20 的最大值。
  2. 计算 100 和 200 的最大值。
  3. 计算用户输入的两个数的最大值。

如果不用方法,就可能反复编写相同代码。

而使用:

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

就可以:

int result1 = max(10, 20);
int result2 = max(100, 200);
int result3 = max(x, y);

所以方法的核心价值可以概括为:

把一个具有明确职责的功能封装起来,并通过调用重复使用。


2.3 方法的基本定义格式

基础阶段可以先掌握:

修饰符 返回值类型 方法名(参数列表) {
    方法体;
    return 返回值;
}

例如:

public static int add(int a, int b) {
    int result = a + b;
    return result;
}

拆开:

public static
    ↓
修饰部分

int
 ↓
返回值类型

add
 ↓
方法名

(int a, int b)
      ↓
参数列表

{
    ...
}
 ↓
方法体

当前 Java 基础语法阶段主要使用:

public static

形式的方法。

这里暂时把它作为基础方法定义格式使用。

static 的完整原理会在后续面向对象章节系统学习。


2.4 方法不会因为“定义了”就自动执行

例如:

public class MethodDemo {
    public static void main(String[] args) {
        System.out.println("main");
    }

    public static void hello() {
        System.out.println("Hello Java");
    }
}

程序只会输出:

main

因为:

hello()

虽然已经定义,却没有被调用。

想执行它,需要:

hello();

完整代码:

public class MethodDemo {
    public static void main(String[] args) {
        System.out.println("开始");

        hello();

        System.out.println("结束");
    }

    public static void hello() {
        System.out.println("Hello Java");
    }
}

执行顺序可以理解为:

main 开始
   ↓
调用 hello()
   ↓
进入 hello()
   ↓
执行 hello 方法体
   ↓
hello 结束
   ↓
返回 main
   ↓
main 继续执行

2.5 无参数、无返回值方法

最简单的方法:

public static void printHello() {
    System.out.println("Hello Java");
}

调用:

printHello();

这里:

()

中没有参数。

而:

void

表示:

这个方法正常完成时不向调用者返回一个结果值。

例如:

public static void printLine() {
    System.out.println("----------------");
}

适合:

打印内容
执行某个动作
修改某些数据
完成某项操作

而不需要把一个结果交回调用位置的场景。


2.6 有参数方法

如果一个方法需要处理外部传入的数据,可以定义参数:

public static void printAge(int age) {
    System.out.println("年龄:" + age);
}

调用:

printAge(22);

执行时可以理解成:

22
 ↓
传入方法
 ↓
age = 22
 ↓
执行方法体

参数让方法从:

只能处理固定数据

升级为:

可以处理不同数据

例如:

printAge(18);
printAge(20);
printAge(22);

使用的是同一个方法,但输入不同。


2.7 形式参数与实际参数

这是必须区分的一组术语。

方法定义:

public static int add(int a, int b) {
    return a + b;
}

其中:

int a
int b

叫:

形式参数(Formal Parameter),简称形参。

调用:

int result = add(10, 20);

其中:

10
20

叫实际参数,更准确地说是:

Argument,通常简称实参。

可以理解为:

方法定义:

add(int a, int b)
        ↑      ↑
       形参    形参


方法调用:

add(10, 20)
     ↑   ↑
    实参 实参

形参负责:

声明这个方法需要接收什么数据。

实参负责:

在某一次具体调用时提供真正的数据。


2.8 参数的个数、类型和顺序需要匹配

定义:

public static void show(String name, int age) {
    System.out.println(name + ":" + age);
}

调用:

show("LingXi", 22);

符合:

String → String
int    → int

而:

show(22, "LingXi");

就不符合该方法的参数列表。

因此调用方法时必须考虑:

方法名
参数个数
参数类型
参数顺序

2.9 有返回值方法

如果方法计算得到一个结果,希望调用者继续使用,就可以返回数据。

例如:

public static int add(int a, int b) {
    return a + b;
}

调用:

int result = add(10, 20);

整个过程:

10、20
  ↓
add()
  ↓
计算 10 + 20
  ↓
return 30
  ↓
调用表达式 add(10,20) 得到 30
  ↓
赋值给 result

因此:

add(10, 20)

本身是一个产生结果的表达式。


2.10 返回值类型

定义:

public static int add(int a, int b)

这里的:

int

表示:

方法返回结果的类型。

因此:

return a + b;

返回的数据必须能够满足该方法声明的返回类型要求。

例如:

public static boolean isAdult(int age) {
    return age >= 18;
}

返回类型:

boolean

所以可以:

boolean result = isAdult(20);

2.11 return 的两个核心作用

return 有两个重要作用。

作用一:返回结果

例如:

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

这里:

return ...

把结果交给调用者。

作用二:立即结束当前方法

例如:

public static void divide(int a, int b) {
    if (b == 0) {
        System.out.println("除数不能为 0");
        return;
    }

    System.out.println(a / b);
}

执行:

return;

后:

当前方法立即结束。

因此下面的:

System.out.println(a / b);

不会继续执行。


2.12 return 值;return;

二者不要混淆。

有返回值:

return result;

既:

结束方法
+
返回一个结果

无返回值方法中:

return;

表示:

只结束当前方法

例如:

public static void checkAge(int age) {
    if (age < 0) {
        System.out.println("年龄不合法");
        return;
    }

    System.out.println("年龄:" + age);
}

这种写法也常被称为:

提前返回(Early Return)。

合理使用提前返回可以减少过深的条件嵌套。


2.13 方法执行结束后会回到调用位置

例如:

public static void main(String[] args) {
    System.out.println("A");

    hello();

    System.out.println("C");
}

public static void hello() {
    System.out.println("B");
}

执行逻辑:

main
 ↓
A
 ↓
调用 hello
 ↓
B
 ↓
hello 结束
 ↓
返回 main 调用位置
 ↓
C

所以输出:

A
B
C

方法调用不是:

跳过去以后再也不回来。

而是:

暂时进入被调用方法,完成后返回调用者继续执行。


2.14 参数变量的生命周期

例如:

public static int add(int a, int b) {
    int result = a + b;
    return result;
}

每调用一次:

add(10, 20);

都会为本次调用创建对应的参数变量:

a
b

下一次:

add(100, 200);

又是另一组本次调用使用的参数变量。

因此不同方法调用之间不会共用同一个局部参数变量。


2.15 Java 参数传递:值传递

Java 方法参数采用:

值传递(Pass by Value)。

在当前基本数据类型阶段尤其容易理解。

例如:

public class PassValueDemo {
    public static void main(String[] args) {
        int number = 10;

        change(number);

        System.out.println(number);
    }

    public static void change(int value) {
        value = 100;
    }
}

调用:

change(number);

可以理解为:

number 当前保存 10
        ↓
把值 10 复制给参数 value
        ↓
value = 10

然后方法内部:

value = 100;

修改的是:

value

而不是调用者的:

number

所以 number 仍然保存:

10

可以用“复印件”理解:

number
  │
  │ 当前值 10
  ↓ 复制
value

修改复印件,不会直接修改原始变量。

引用类型参数仍然遵守值传递,只不过传递的值与对象引用有关,这部分将在数组和面向对象阶段继续学习。


2.16 什么是方法重载

方法重载(Overloading)表示:

同一个类中可以存在多个同名方法,只要它们的方法签名能够区分。

基础阶段重点看参数列表。

例如:

public static void print(int value) {
    System.out.println(value);
}

public static void print(String value) {
    System.out.println(value);
}

public static void print(int a, int b) {
    System.out.println(a + "," + b);
}

三个方法都叫:

print

但是参数列表不同。

所以构成方法重载。


2.17 哪些差异能够构成重载

常见三种情况:

参数个数不同

public static void print(int a) {
}

public static void print(int a, int b) {
}

参数类型不同

public static void print(int value) {
}

public static void print(String value) {
}

参数类型顺序不同

public static void print(int a, double b) {
}

public static void print(double a, int b) {
}

都可以构成重载。


2.18 仅修改参数名不能构成重载

下面两个方法:

public static void add(int a, int b) {
}

public static void add(int x, int y) {
}

不能同时存在。

因为从方法调用者角度:

add(10, 20);

根本无法通过:

a/b

还是:

x/y

区分两个方法。

参数名称不是判断重载的关键。


2.19 仅修改返回值类型不能构成重载

下面也是错误的:

public static int getValue() {
    return 1;
}

public static double getValue() {
    return 1.0;
}

调用:

getValue();

编译器不能仅根据:

你希望拿到 int 还是 double

来把它们当作两个普通重载方法。

因此:

返回值类型本身不能作为方法重载的区分依据。


2.20 调用重载方法时会发生什么

例如:

public static void print(int value) {
    System.out.println("int");
}

public static void print(String value) {
    System.out.println("String");
}

调用:

print(10);

编译器根据:

方法名 print
实参个数 1
实参类型 int

找到合适的方法:

print(int value)

而:

print("Java");

则匹配:

print(String value)

因此方法重载的选择主要发生在:

编译期。


三、使用方法

3.1 两个整数求和

public class MethodDemo {
    public static void main(String[] args) {
        int result = add(10, 20);

        System.out.println(result);
    }

    public static int add(int a, int b) {
        return a + b;
    }
}

3.2 判断奇偶数

public static boolean isEven(int number) {
    return number % 2 == 0;
}

调用:

boolean result = isEven(10);

System.out.println(result);

3.3 获取最大值

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

3.4 打印指定次数

public static void printHello(int count) {
    for (int i = 0; i < count; i++) {
        System.out.println("Hello Java");
    }
}

这里用到了循环;如果尚未学习循环,可以先把它作为后续会理解的案例。


3.5 提前结束方法

public static void divide(int a, int b) {
    if (b == 0) {
        System.out.println("除数不能为 0");
        return;
    }

    System.out.println(a / b);
}

重点不是背代码,而是理解:

非法情况
 ↓
提前处理
 ↓
return
 ↓
正常主逻辑继续保持简单

3.6 方法重载

public class OverloadDemo {
    public static void main(String[] args) {
        print(100);
        print("Java");
        print(10, 20);
    }

    public static void print(int value) {
        System.out.println(value);
    }

    public static void print(String value) {
        System.out.println(value);
    }

    public static void print(int a, int b) {
        System.out.println(a + "," + b);
    }
}

对于使用者:

都叫 print

而编译器根据参数选择合适版本。

这就是重载给 API 带来的一个重要价值:

对“同类功能”使用统一的方法名称。


四、原理与进阶

4.1 方法签名与方法重载

在 Java 规范中,方法重载不是简单地判断:

方法名一样 + 随便哪里不一样

而是建立在:

方法签名(Method Signature)

之上。

基础阶段重点理解:

方法名
+
参数类型

以及相关参数列表信息。

返回值类型不属于普通重载选择的核心区分依据。

因此:

print(int)
print(String)

可以重载。

而:

int get()
double get()

不能只靠返回类型形成两个可区分的方法。


4.2 重载选择并不只是“类型完全相等”

例如:

public static void test(long value) {
    System.out.println("long");
}

public static void main(String[] args) {
    test(10);
}

虽然:

10

默认是 int

但:

int → long

可以进行扩大转换。

所以这个方法仍然能够被调用。

随着未来学习:

包装类
自动装箱
可变参数
泛型

重载解析会更加复杂。

基础阶段不需要背完整算法,但必须知道:

编译器是在可适用的方法中按照 Java 的重载解析规则选择最合适的方法,而不只是做字符串式的参数匹配。


4.3 方法调用与调用栈的初步认识

例如:

main()
  ↓
calculate()
  ↓
max()

当一个方法调用另一个方法时,程序需要保存:

当前执行到了哪里
局部变量
参数
返回位置

然后进入下一个方法。

可以先建立一个抽象模型:

main
└── calculate
    └── max

max 完成后:

max 返回
↓
calculate 继续
↓
calculate 返回
↓
main 继续

真正的 JVM 栈帧等内部机制属于后续更深入的 JVM 学习内容,本章不需要过度下钻。


4.4 方法应该承担单一、明确的职责

一个方法如果同时:

读取输入
计算 BMI
判断健康等级
发送网络请求
写数据库
打印结果

通常说明职责太多。

更合理的设计可能是:

readInput()
calculateBMI()
judgeLevel()
printResult()

这体现一个重要工程思想:

一个方法应该尽量完成一个明确的职责。

这种设计通常会提升:

可读性
复用性
可测试性
可维护性

五、实践应用

方法是所有 Java 项目最基本的代码组织单位之一。

例如学生管理:

addStudent()
deleteStudent()
findStudent()
updateStudent()

用户系统:

login()
register()
checkPassword()

订单系统:

calculatePrice()
createOrder()
cancelOrder()

算法程序:

binarySearch()
quickSort()
isPrime()

未来学习对象之后,这些方法还会进一步变成:

对象的行为
Service 业务逻辑
Controller 请求处理
Mapper 数据访问

所以真正需要形成的能力不是:

会背方法定义格式。

而是:

看见一个功能,能够判断是否应该把它抽成一个独立方法。


六、常见问题

6.1 方法定义在 main 里面吗?

普通 Java 方法不能像这样定义:

public static void main(String[] args) {
    public static void test() {
    }
}

方法通常作为类的成员定义。

例如:

public class Demo {
    public static void main(String[] args) {
    }

    public static void test() {
    }
}

6.2 方法一定需要参数吗?

不一定。

public static void hello() {
}

就是无参数方法。

参数是否存在取决于:

这个功能是否需要调用者提供外部数据。


6.3 方法一定要返回数据吗?

不一定。

例如:

public static void printHello() {
    System.out.println("Hello");
}

返回类型使用:

void

即可。


6.4 void 方法能使用 return 吗?

可以使用:

return;

提前结束方法。

但不能像有返回值方法那样:

return 100;

向调用者返回一个普通结果值。


6.5 return 后还能继续写正常执行的语句吗?

如果一条语句确定永远位于无条件 return 之后,就会成为不可达代码。

例如:

return;

// 永远不可能执行
System.out.println("Hello");

这种代码不能作为正常逻辑存在。


6.6 形参和实参是不是同一个变量?

不是。

例如:

int number = 10;
change(number);

方法:

public static void change(int value) {
}

numbervalue 是两个不同变量。

调用时,参数变量会接收实参表达式计算得到的值。


6.7 Java 是引用传递还是值传递?

Java 方法调用采用值传递。

基本类型容易理解:

复制基本数据值

引用类型同样是值传递,只不过复制的是引用相关的值。

不要形成:

基本类型是值传递
对象是引用传递

这种错误二分法。

引用类型的具体行为将在对象和数组章节继续理解。


6.8 仅修改返回值类型能构成重载吗?

不能。

例如:

int get()
double get()

不能仅凭返回值类型区分为合法的普通重载。


6.9 参数名称不同算重载吗?

不算。

test(int a)
test(int b)

方法参数名称不同,但调用方式无法区分。


6.10 方法是不是越多越好?

不是。

不应该把:

每一两行代码

机械拆成一个方法。

是否拆分主要看:

职责是否独立
是否会重复使用
是否降低复杂度
是否提高可读性
是否便于测试

七、练习与验收

7.1 知识问答

  1. 什么是方法?为什么方法可以理解成一个独立功能单元?
  2. 方法定义通常包含哪些部分?
  3. 方法为什么必须调用后才会执行?
  4. 什么是形参?什么是实参?
  5. 参数个数、类型和顺序为什么重要?
  6. 什么叫返回值?
  7. void 表示什么?
  8. return 数据;return; 有什么区别?
  9. Java 参数传递采用什么机制?
  10. 为什么修改基本类型形参不会直接修改调用者的变量?
  11. 什么是方法重载?
  12. 哪三类参数列表差异经常可以构成重载?
  13. 仅修改形参名称能否形成重载?
  14. 仅修改返回值类型能否形成重载?
  15. 为什么合理拆分方法能够提升代码质量?

7.2 代码阅读

禁止运行:

public class MethodRead01 {
    public static void main(String[] args) {
        int x = max(10, 20);

        System.out.println(x);

        print(x);
    }

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

    public static void print(int value) {
        if (value < 0) {
            return;
        }

        System.out.println(value);
    }
}

要求:

  1. 按执行顺序描述 main → max → main → print → main
  2. max(10, 20) 的结果是什么?
  3. 如果删除 max 中的 return 会发生什么?
  4. print() 中的 return; 有什么作用?

继续分析:

public class PassValueRead {
    public static void main(String[] args) {
        int number = 10;

        change(number);

        System.out.println(number);
    }

    public static void change(int value) {
        value = 100;
    }
}

要求:

  1. 不运行程序预测输出。
  2. 画出 number → value 的参数传递过程。
  3. 解释为什么修改 value 不等于修改 number

7.3 手写代码

  1. 手写 add(int a, int b)
  2. 手写 max(int a, int b)
  3. 手写 isEven(int number)
  4. 手写一个无参数无返回值 printHello()
  5. 手写一个 divide(int a, int b),当 b == 0 时提前结束。
  6. 设计三个同名 print 方法,分别接收 intString、两个 int
  7. 把一个包含输入、计算和输出的长 main 拆分成至少三个职责明确的方法。

7.4 Debug

下面方法无法正常通过编译:

public static int add(int a, int b) {
    System.out.println(a + b);
}

要求:

  1. 找出问题。
  2. 区分“打印结果”和“返回结果”。
  3. 根据不同需求设计两种合理修改方案。

继续:

public static int test(int a) {
    return a;
}

public static double test(int b) {
    return b;
}

要求:

  1. 判断是否构成合法重载。
  2. 说明理由。
  3. 在保留方法名 test 的前提下设计合法重载。

7.5 综合训练

重构一个控制台“成绩分析程序”。

要求将程序拆成:

readScore()
isValidScore()
getLevel()
printResult()

或者你认为更合理的方法。

要求:

  1. 每个方法职责明确。
  2. 合理设计参数和返回值。
  3. 至少存在一个有返回值方法。
  4. 至少存在一个 void 方法。
  5. 至少使用一次提前 return
  6. 完成后说明为什么这样拆分,而不是把所有代码放进 main

7.6 本章验收

关闭资料后确认:

  • [ ] 能从零写出一个完整方法。
  • [ ] 能正确设计方法名、参数和返回类型。
  • [ ] 能区分形参与实参。
  • [ ] 能说明调用方法后的控制流。
  • [ ] 能使用 return 返回数据。
  • [ ] 能使用 return; 提前结束方法。
  • [ ] 能解释 Java 的值传递。
  • [ ] 能判断两个方法是否构成重载。
  • [ ] 知道返回值类型不能单独构成重载。
  • [ ] 能将一个过长的 main 主动拆成多个小方法。

如果只能背:

public static 返回值 方法名(...)

却不能根据具体需求决定:

要不要参数
要不要返回值
方法应该负责什么

那么还没有真正掌握方法。