杰瑞科技汇

Java参数传参是值传递还是引用传递?

什么是可变参数?

可变参数(Varargs)是 Java 1.5 引入的一个特性,它允许一个方法接受零个或多个指定类型的参数,在方法签名中,可变参数使用省略号(...)表示。

它让你可以定义一个方法,可以灵活地传入任意数量的同类型参数,而不用为每一种可能的参数数量都重载一个方法。


语法

可变参数的语法非常简单,在方法参数的类型后面加上 即可。

public void methodName(DataType... parameterName) {
    // 方法体
}

关键点:

  1. 类型后跟 DataType 是参数的类型, 表示这是一个可变参数。
  2. 只能有一个可变参数:一个方法中只能有一个可变参数,并且它必须是方法的最后一个参数,这是因为编译器需要知道哪个参数是可变的,哪个是固定的。
    • 正确: public void doSomething(String name, int... scores)
    • 错误: public void doSomething(int... scores, String name)
  3. 本质上是一个数组:当你在一个方法内部使用可变参数时,它会被当作一个数组来处理。int... scores 在方法内部就是 int[] scores

如何使用?

我们通过几个例子来理解它的用法。

示例 1:基本用法

这是一个最简单的例子,计算一系列整数的总和。

public class VarargsExample {
    // 定义一个可变参数方法
    public static int sum(int... numbers) {
        // 在方法内部,numbers 被当作一个 int[] 数组
        System.out.println("传入的参数个数: " + numbers.length);
        int total = 0;
        for (int number : numbers) {
            total += number;
        }
        return total;
    }
    public static void main(String[] args) {
        // 调用可变参数方法,可以传入任意数量的参数
        int sum1 = sum(); // 传入0个参数
        System.out.println("sum1 = " + sum1); // 输出: sum1 = 0
        int sum2 = sum(10); // 传入1个参数
        System.out.println("sum2 = " + sum2); // 输出: sum2 = 10
        int sum3 = sum(10, 20, 30); // 传入3个参数
        System.out.println("sum3 = " + sum3); // 输出: sum3 = 60
        int sum4 = sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 传入10个参数
        System.out.println("sum4 = " + sum4); // 输出: sum4 = 55
    }
}

输出:

传入的参数个数: 0
sum1 = 0
传入的参数个数: 1
sum2 = 10
传入的参数个数: 3
sum3 = 60
传入的参数个数: 10
sum4 = 55

示例 2:混合普通参数和可变参数

可变参数必须是最后一个参数,所以它可以和普通参数一起使用。

public class MixedVarargsExample {
    // 第一个参数是固定的,后面是可变数量的字符串
    public static void printStudentInfo(String studentId, String... courses) {
        System.out.println("学生ID: " + studentId);
        System.out.println("选课列表: ");
        if (courses.length == 0) {
            System.out.println("  该学生没有选课。");
        } else {
            for (String course : courses) {
                System.out.println("  - " + course);
            }
        }
        System.out.println("----------------------");
    }
    public static void main(String[] args) {
        printStudentInfo("S001"); // 只传ID,不传可变参数
        printStudentInfo("S002", "Java编程");
        printStudentInfo("S003", "数据结构", "算法分析", "计算机网络");
    }
}

输出:

学生ID: S001
选课列表:
  该学生没有选课。
----------------------
学生ID: S002
选课列表:
  - Java编程
----------------------
学生ID: S003
选课列表:
  - 数据结构
  - 算法分析
  - 计算机网络
----------------------

可变参数的重载规则

当可变参数方法与普通方法共存时,Java 的重载规则有时会让人困惑,核心原则是:编译器会选择最“具体”、“明确”的那个方法。

public class OverloadExample {
    // 方法1:普通方法
    public static void print(String s1, String s2) {
        System.out.println("调用普通双参数方法: " + s1 + ", " + s2);
    }
    // 方法2:可变参数方法
    public static void print(String... strings) {
        System.out.println("调用可变参数方法,参数个数: " + strings.length);
        for (String s : strings) {
            System.out.print(s + " ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        print("Hello", "World"); // 编译器会选择哪个?
        // 情况1:如果传入的参数数量与某个普通方法完全匹配,优先调用普通方法。
        // 因为普通方法比可变参数方法更“具体”。
        // 输出: 调用普通双参数方法: Hello, World
        print("JustOne"); // 只有一个参数,没有匹配的普通方法,只能调用可变参数方法。
        // 输出: 调用可变参数方法,参数个数: 1
        // JustOne
        print(); // 没有参数,只能调用可变参数方法。
        // 输出: 调用可变参数方法,参数个数: 0
    }
}

关键点:

  • 精确匹配优先:如果调用时传入的参数数量和某个非可变参数方法(重载方法)的参数数量完全一致,那么那个非可变参数方法会被优先调用。
  • 可变参数作为“兜底”选项:当没有精确匹配的普通方法时,编译器才会选择可变参数方法。

底层实现原理

从 Java 1.5 开始, 语法糖被引入,但它并不是一种全新的机制,在编译阶段,编译器会将可变参数语法转换成数组

我们写的 public void sum(int... numbers),在编译后,其字节码实际上等同于 public void sum(int[] numbers)

这就是为什么在方法内部,你可以像操作数组一样操作可变参数(numbers.lengthfor (int n : numbers))。

注意:这个转换只发生在调用方方法定义之间,对于方法本身来说,它接收到的就是一个数组,你不能在方法签名中同时写 int...int[],因为它们在编译后是一样的,会导致编译错误。

// 这是错误的,因为 int... 和 int[] 在底层是等价的
// public void myMethod(int[] a, int... b) {} // 编译失败

何时使用可变参数?

可变参数非常适合以下场景:

  1. 工具类方法:如 java.util.Arrays.asList(T... a)System.out.println(Object... args)
  2. 参数数量不确定的方法:当你无法预调用者会传入多少个参数时,可变参数提供了极大的灵活性。
  3. 替代方法重载:当你需要为同一个功能提供不同参数数量的版本时,可变参数可以让你只写一个方法,而不是多个重载方法。

何时避免使用可变参数?

  1. 性能敏感的场景:每次调用可变参数方法时,都会创建一个新的数组,如果这个方法被频繁调用,可能会带来微小的性能开销,对于性能要求极高的代码,可以考虑重载方法。
  2. 参数类型不同:可变参数要求所有参数的类型必须一致,如果需要处理不同类型的参数,可变参数不适用。
  3. 参数数量通常固定:如果方法的参数数量几乎总是固定的(比如一个方法总是接收3个坐标点),那么定义3个明确的参数比使用可变参数更清晰、更符合直觉。
特性 描述
语法 在参数类型后加 ,如 int... numbers
数量限制 一个方法只能有一个可变参数,且必须是最后一个
本质 在方法内部,可变参数被视为一个数组
调用方式 可以传入零个、一个或多个同类型参数
重载规则 优先匹配参数数量固定的普通方法,没有匹配时才使用可变参数方法
优点 灵活,减少方法重载,代码简洁
缺点 可能有微小性能开销,可能使方法意图不够明确

希望这个详细的解释能帮助你完全理解 Java 的可变参数!

分享:
扫描分享到社交APP
上一篇
下一篇