学习前提
理解Java基本类型、条件和循环;本课引入方法参数和数组契约。
本课完成后你能做什么
拆分可复用计算,理解参数和返回值。
先理解机制
方法签名声明参数类型和返回类型。将求和从main中提取后,可以用不同输入单独验证,而不用复制循环。方法应明确是否允许空数组、null和负值。
Java总是按值传递参数。数组参数传递的是引用的副本;通过它修改元素会影响调用者看到的同一个数组,但把参数重新赋值不等于替换调用者的变量。
本例sum只读取输入并返回long,避免修改数组。空数组的和定义为0;null被明确拒绝。这些边界属于接口约定,不应让调用者猜测。
可运行示例
使用JDK 21。将代码保存为Main.java,在独立练习目录执行 javac -encoding UTF-8 Main.java,再执行 java Main。每章独立运行,不把多个public Main类放进同一文件。
public class Main {
static long sum(int[] values) {
if (values == null) throw new IllegalArgumentException("values required");
long total = 0;
for (int value : values) total += value;
return total;
}
public static void main(String[] args) {
int[] minutes = {25, 30, 15};
System.out.println(sum(minutes));
System.out.println(sum(new int[0]));
System.out.println(minutes[0]);
}
}
推演与验证
输出70、0、25。第三行验证函数未改写第一个元素。
常见错误与边界
返回long不代表任意数量都不会溢出;它只是扩大范围。不要在求和函数中混入打印、读取输入等职责。
动手练习
编写countAtLeast(int[] values,int minimum),统计大于等于阈值的元素。
参考解法与理由
沿用null检查,计数从0开始,循环中if(value>=minimum)增加计数并返回。对{25,30,15}和阈值25结果为2;空数组为0。
深入实验:Java传值为什么仍能修改数组
“Java传引用”容易误导:参数得到的是引用值的副本。副本仍能指向调用者的数组,所以元素修改可见;把参数重新指向另一个数组却不会重绑调用者变量。保存为Main.java,使用JDK 21独立编译运行。
import java.util.Arrays;
public class Main {
static void change(int[] values) {
values[0] = 9;
values = new int[]{100, 200};
values[1] = 300;
}
public static void main(String[] args) {
int[] original = {1, 2};
change(original);
System.out.println(Arrays.toString(original));
int[] copy = Arrays.copyOf(original, original.length);
copy[0] = 0;
System.out.println(Arrays.toString(original));
System.out.println(Arrays.toString(copy));
}
}
预期观察
三行分别为[9, 2]、[9, 2]、[0, 2]。调用者没有得到[100, 300]。
原因、取舍与适用边界
第一次赋值通过共享引用改了同一个数组。第二次赋值只替换方法里的局部参数,调用者仍指向旧数组。对int[]复制后元素独立,但对对象数组仅复制引用;对象内部仍可能共享可变状态。编写API时应明确“会修改传入数据”还是“返回新值”;不要让调用者通过试错猜测副作用。
进阶练习
设计一个不修改原数组的doubleValues方法,处理空数组并说明null的约定。
解法与推理
先Objects.requireNonNull检查,再创建等长新数组并逐项使用Math.multiplyExact(value, 2)填入;空数组返回空数组。multiplyExact在溢出时报错,避免把大整数静默变成负数。方法文档应说明null和算术溢出都会失败。