CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Java 语言基础/课时/方法与参数
RawBlame
课时目录14 节

Java 语言基础

  1. 01Java 与 JVM 是什么
  2. 02基本语法与 main
  3. 03基本类型与引用类型
  4. 04条件与循环
  5. 05方法与参数
  6. 06数组
  7. 07类与封装
  8. 08继承接口与多态
  9. 09集合框架入门
  10. 10字符串
  11. 11异常处理
  12. 12文件 IO 入门
  13. 13Maven 与项目结构
  14. 14综合练习与 dsa-java 分工

方法与参数

重载、可变参数、static 入门。

35 分钟

方法与参数

这一课你将能做到

  1. 按规范定义方法(返回值、参数、命名),把重复逻辑抽成可复用单元
  2. 用一句话说清「Java 只有值传递」——并用代码验证基本类型和对象参数的区别
  3. 会用方法重载、可变参数和递归,写出与 Spring Service 同风格的方法签名

为什么要学

方法是代码组织的最小复用单元。本站后端一个 Service 方法通常长这样:

public Order createOrder(Long userId, Long productId, int quantity) { ... }

控制器调它,事务包着它,单元测试测它。方法设计得好,每个方法只干一件事、名字见名知意,别人读你的代码像读文章;设计得差,一个方法三百行,改一处崩全局。

这一课还有一个必考点:参数传递机制。面试官一句「Java 是值传递还是引用传递」能筛掉一半人——答案将在步骤 3 用代码证实。

先建立图像

方法的解剖图:

修饰符  返回类型  方法名(参数列表) { 方法体 }
public   int      add(int a, int b) { return a + b; }
  │        │        │       │
  谁能调   交回什么  叫什么   收什么

调用像「寄快递 + 等回执」:

main:  int total = add(3, 5);      ──寄出 3 和 5──▶  add 方法里 a=3, b=5
                                                          │ 计算 8
main:  total = 8                    ◀──寄回 8──────────   return a + b;

跟着做

步骤 1:定义并调用第一个方法

新建 MethodDemo.java:

public class MethodDemo {
    public static void main(String[] args) {
        int total = add(3, 5);              // 调用:实参 3、5 传给形参 a、b
        System.out.println("3 + 5 = " + total);

        greet("tom");                       // 无返回值方法,单独成语句调用
        printLine('=', 20);

        double avg = average(88, 92, 75);
        System.out.printf("平均分: %.1f%n", avg);
    }

    static int add(int a, int b) {          // 有返回值:必须 return
        return a + b;
    }

    static void greet(String name) {        // void:不返回东西
        System.out.println("你好, " + name);
    }

    static void printLine(char ch, int count) {
        for (int i = 0; i < count; i++) {
            System.out.print(ch);
        }
        System.out.println();
    }

    static double average(int... scores) {  // 可变参数,步骤 5 详解
        if (scores.length == 0) return 0;
        int sum = 0;
        for (int s : scores) sum += s;
        return (double) sum / scores.length;
    }
}

输出:

3 + 5 = 8
你好, tom
====================
平均分: 85.0

步骤 2:方法返回值规则

public class ReturnDemo {
    public static void main(String[] args) {
        System.out.println(findUserStatus(-1));
        System.out.println(findUserStatus(1001));
    }

    static String findUserStatus(long userId) {
        if (userId <= 0) {
            return "非法用户 ID";        // 提前返回:卫语句风格,本站后端标准写法
        }
        // ... 这里假装查了数据库
        return "在线";
    }

    static double divide(int a, int b) {
        if (b == 0) {
            System.out.println("除数不能为 0");
            return Double.NaN;           // 所有分支都必须有返回值,编译器强制检查
        }
        return (double) a / b;
    }
}

规则:返回类型不是 void 的方法,每条执行路径都必须 return;return 一执行,方法立即结束。

步骤 3:值传递实证(本课最重要实验)

public class PassByValue {
    public static void main(String[] args) {
        int n = 10;
        changeInt(n);
        System.out.println("基本类型 after: " + n);   // 还是 10!

        int[] arr = {1, 2, 3};
        changeArray(arr);
        System.out.println("数组 after: " + java.util.Arrays.toString(arr)); // [99, 2, 3]

        int[] arr2 = {1, 2, 3};
        reassignArray(arr2);
        System.out.println("数组 after2: " + java.util.Arrays.toString(arr2)); // [1, 2, 3]
    }

    static void changeInt(int x) {
        x = 999;         // 改的是「复印件」,外面的 n 毫发无损
    }

    static void changeArray(int[] a) {
        a[0] = 99;       // 复印的是「地址」,顺着地址改到了同一个对象 → 生效
    }

    static void reassignArray(int[] a) {
        a = new int[]{7, 7, 7};   // 改的是复印件本身(指向了新对象)→ 外面看不见
    }
}

输出:

基本类型 after: 10
数组 after: [99, 2, 3]
数组 after2: [1, 2, 3]

结论:传进去的永远是变量的复印件——基本类型复印「值」,引用类型复印「地址」。顺着地址改对象内容有效,把复印件改指向新对象则无效。这就是「Java 只有值传递」的完整含义。

步骤 4:方法重载(Overload)

public class OverloadDemo {
    public static void main(String[] args) {
        System.out.println(format(100));          // 调 int 版本
        System.out.println(format(3.14159));      // 调 double 版本
        System.out.println(format("ORDER-1001")); // 调 String 版本
    }

    // 同名不同参:参数个数、类型或顺序不同即构成重载
    static String format(int n)       { return "整数: " + n; }
    static String format(double d)    { return "小数: " + d; }
    static String format(String s)    { return "字符串: " + s; }
}

注意:只有返回类型不同不构成重载,编译直接报错。JVM/编译器靠「参数列表」决定调谁。

步骤 5:可变参数(varargs)

public class VarargsDemo {
    public static void main(String[] args) {
        System.out.println(join("-"));                  // 空
        System.out.println(join("-", "a", "b", "c"));   // a-b-c
        System.out.println(join(",", "mysql", "redis")); // mysql,redis
    }

    // String... 本质是 String[],调用方想传几个传几个
    static String join(String sep, String... parts) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parts.length; i++) {
            if (i > 0) sb.append(sep);
            sb.append(parts[i]);
        }
        return sb.toString();
    }
}

规则:可变参数必须是参数列表的最后一个;一个方法最多一个可变参数。

步骤 6:递归入门

public class RecursionDemo {
    public static void main(String[] args) {
        System.out.println(factorial(5));    // 120
        System.out.println(fib(10));         // 55
    }

    // 阶乘:5! = 5 × 4! = ... = 5 × 4 × 3 × 2 × 1
    static long factorial(int n) {
        if (n <= 1) return 1;                // 递归出口,没有它必栈溢出
        return n * factorial(n - 1);
    }

    // 斐波那契(演示用;实际有大量重复计算,学习 dsa-java 时会优化)
    static long fib(int n) {
        if (n <= 2) return 1;
        return fib(n - 1) + fib(n - 2);
    }
}

递归两要素:出口(终止条件)+ 递推(缩小问题规模)。缺出口就是 StackOverflowError。

讲清楚要点

  1. 方法必须属于类:Java 没有「独立函数」,哪怕是工具方法也要挂在某个类里(配合 static)。
  2. 形参是复印件,实参不受影响:除非你顺着引用改对象内部状态。想「真的改变」外部变量,用返回值接回来,或传可变容器。
  3. 重载解决「同一个动作、不同入参」:如 System.out.println 本身就有几十个重载版本。
  4. void 方法也能提前 return;:用于「条件不满足就结束」,不带任何值。
  5. static 方法属于类:调用写 类名.方法名(),内部不能直接访问非 static 成员(因为非 static 成员需要对象才存在)。
  6. 方法命名用动词开头小驼峰:getUserById、createOrder、isValid。本站后端还有惯例:查询用 get/find/query,判断用 is/has,避免一个方法既查询又修改。

你可能会懵的地方

  • Java 到底是值传递还是引用传递? 只有值传递。引用类型的「值」是对象地址,复印件也指向同一对象,所以看起来像「引用传递」。判断标准:方法内让参数指向新对象,外部是否受影响——不受影响,就证明是值传递。

  • 方法重载和重写是一回事吗? 不是。重载(overload):同一个类里同名不同参,编译期决定;重写(override):子类覆盖父类同签名方法,运行期决定(下一课细讲)。中文相似,英文和机制都不同。

  • 可变参数和数组参数什么关系? String... parts 完全等价于 String[] parts,只是调用端语法糖。已有 String[] 变量时可直接传入。

  • 为什么工具方法都加 static? 工具方法不依赖对象状态(无实例字段),加 static 可以直接 Math.max(1, 2) 这样调用,不必 new 对象。Spring 的 Bean 方法反而是实例方法——因为它们依赖注入的字段状态。

  • 递归会不会一直占内存? 每层调用在虚拟机栈里压一个栈帧。深度过大(如 factorial(100000))触发 StackOverflowError。能用循环就用循环,递归适合「天然树形/分形」问题。

错法对照

错法问题正解
static int add(int a, int b) { System.out.println(a+b); } 声明返回 int 却没 return编译错误补 return a + b; 或改返回 void
期望 changeInt(n) 改变外部 n值传递,外面不变用返回值:n = change(n)
重载只改返回类型不构成重载,编译错参数列表必须不同
static void join(String... parts, String sep)可变参数必须在最后调整为 join(String sep, String... parts)
递归没写出口StackOverflowError第一步先写终止条件
方法名叫 do1、process2无法从名字看懂用途动词+名词小驼峰,见名知意

卡点急救

  1. 编译报「缺少返回语句」:if 分支 return 了、if 外没写兜底 return。编译器按「所有路径」检查,补全 else 或方法末尾的 return。
  2. 改了参数外面没变化:回看步骤 3,值传递是语言机制不是 bug。需要改变量就 return 新值接住。
  3. 调用方法报「无法从静态上下文引用非静态方法」:main 是 static,直接调了非 static 方法。给该方法加 static,或先创建对象再调用。
  4. 重载调用歧义:如 format(null) 时 String 版本和 char[] 版本都匹配报错,强制转型 format((String) null) 明确意图——实际代码应避免这种设计。

面试常问

  • Java 是值传递还是引用传递?(必考) 只有值传递。基本类型复制值,引用类型复制地址;方法内重新赋值参数不影响外部,顺着地址修改对象内容才影响。标准论证代码即本课步骤 3。

  • 重载和重写的区别? 重载:同类中同名不同参,编译期静态绑定;重写:父子类间同签名方法,运行期动态绑定,依赖 @Override 保证签名一致。

  • 构造器能重载吗? 能,而且非常常见(如 new StringBuilder() 与 new StringBuilder(32)),构造器本质就是「类名做方法名的特殊方法」。

  • 可变参数会带来什么坑? 与固定参数重载共存时可能歧义;null 直接传入会被当成 null 数组导致 NPE;反射场景 varargs 处理麻烦。团队规范通常优先固定参数重载。

  • 什么是栈帧? 方法调用时 JVM 在虚拟机栈中分配的帧,存局部变量表、操作数栈、返回地址。方法结束栈帧出栈——这也是「局部变量在栈上」说法的由来。

自测三问

  1. 用一段代码(不加解释)向面试官证明 Java 只有值传递。
  2. 下面两个方法构成重载吗:int f(int a) 与 double f(int a)?为什么?
  3. 递归计算 1+2+...+n,出口和递推分别是什么?n 很大时会发生什么?

小结一句话

方法 = 有名字的可复用代码块,靠参数列表区分重载;Java 只传「复印件」(值或地址),改对象内部有效、换指向无效;递归先写出口,可变参数必须排最后。

延伸

  • Oracle:定义方法
  • 下一课:数组——批量数据的容器,Arrays 工具类全家桶,以及数组与集合的爱恨情仇。
加载本节练习…
← 上一课条件与循环下一课 →数组