跳到内容

第 1 卷 / 第 5 章 / 第 8 课

5.8 实验:方法、值传递与重载 ​

前置知识:方法声明、参数、返回值、调用栈和重载

预计时间:50 分钟

练习环境:JDK 17+ 与 Bash;所有文件写入临时目录

完成标志:完成四项必做实验并解释调用契约

bash
chapter5_lab_root=$(mktemp -d)
readonly chapter5_lab_root
mkdir -p "$chapter5_lab_root/chapter05-lab/out"
cd "$chapter5_lab_root/chapter05-lab"

必做一:纯计算方法 ​

保存为 NumberTools.java:

java
public class NumberTools {
    static boolean isEven(int value) {
        return value % 2 == 0;
    }

    static int maxOfThree(int first, int second, int third) {
        return Math.max(first, Math.max(second, third));
    }

    static int digitSum(int value) {
        long remaining = Math.abs((long) value);
        int sum = 0;
        do {
            sum += (int) (remaining % 10);
            remaining /= 10;
        } while (remaining > 0);
        return sum;
    }

    public static void main(String[] args) {
        System.out.println(isEven(-4));
        System.out.println(maxOfThree(3, 9, 5));
        System.out.println(digitSum(-2147483648));
    }
}
bash
javac -d out NumberTools.java
java -cp out NumberTools

预期为 true、9、47。digitSum 先转 long 再取绝对值,避开 Math.abs(Integer.MIN_VALUE) 仍为负数的边界。

必做二:证明值传递 ​

保存为 PassingLab.java:

java
import java.util.Arrays;

public class PassingLab {
    static void changePrimitive(int value) {
        value = 99;
    }

    static void mutateArray(int[] values) {
        values[0] = 99;
    }

    static void replaceArray(int[] values) {
        values = new int[] {7, 8};
    }

    public static void main(String[] args) {
        int count = 1;
        int[] numbers = {1, 2};

        changePrimitive(count);
        mutateArray(numbers);
        replaceArray(numbers);

        System.out.println(count);
        System.out.println(Arrays.toString(numbers));
    }
}
bash
javac -d out PassingLab.java
java -cp out PassingLab

预期:

text
1
[99, 2]

用“复制了什么值、两个变量是否标识同一对象”解释,不使用“数组是引用传递”这句错误捷径。

必做三:重载解析 ​

保存为 OverloadLab.java:

java
public class OverloadLab {
    static String pick(long value) {
        return "long";
    }

    static String pick(Integer value) {
        return "Integer";
    }

    static String pick(int... values) {
        return "varargs";
    }

    static String describe(Object value) {
        return "object";
    }

    static String describe(String value) {
        return "string";
    }

    public static void main(String[] args) {
        Object value = "iron";
        System.out.println(pick(1));
        System.out.println(pick());
        System.out.println(describe(value));
        System.out.println(describe("iron"));
    }
}
bash
javac -d out OverloadLab.java
java -cp out OverloadLab

预期:long、varargs、object、string。记录每个调用点的实参声明类型和适用阶段。

必做四:栈轨迹 ​

保存为 TraceLab.java:

java
public class TraceLab {
    static void loadOrder() {
        parseQuantity();
    }

    static void parseQuantity() {
        Integer.parseInt("many");
    }

    public static void main(String[] args) {
        loadOrder();
    }
}
bash
javac -d out TraceLab.java
trace_status=0
java -cp out TraceLab || trace_status=$?
test "$trace_status" -ne 0

运行应以非零状态结束并打印 NumberFormatException。从 parseQuantity、loadOrder 到 main 标记业务调用链;错误消息和 JDK 内部帧可能随版本变化。

选做一:有界递归 ​

java
static long factorial(int value) {
    if (value < 0) {
        throw new IllegalArgumentException("negative value");
    }
    if (value > 20) {
        throw new ArithmeticException("factorial exceeds long: " + value);
    }
    if (value <= 1) {
        return 1L;
    }
    return Math.multiplyExact(value, factorial(value - 1));
}

测试 0、1、5、20、21 和 -1。21 的阶乘超出 long,输入检查会立即报告;负数则违反阶乘的输入域。再传入 Integer.MAX_VALUE,确认它也在递归前被拒绝。

选做二:把默认参数集中 ​

java
static String describeItem(String name) {
    return describeItem(name, 1, false);
}

static String describeItem(String name, int level) {
    return describeItem(name, level, false);
}

static String describeItem(String name, int level, boolean rush) {
    if (name == null || name.isBlank()) {
        throw new IllegalArgumentException("name is blank");
    }
    if (level < 1) {
        throw new IllegalArgumentException("level: " + level);
    }
    return name + ":" + level + ":" + rush;
}

验证所有重载都走同一校验。再思考 rush 是否应改为枚举。

故障实验 ​

逐个打开并记录第一条诊断:

java
// static int convert(int value) { return value; }
// static long convert(int number) { return number; } // 只改返回类型
java
// static void print(String value) { }
// static void print(Integer value) { }
// print(null); // 歧义
java
// static int missingReturn(boolean flag) {
//     if (flag) return 1;
// }

区分重复签名、重载歧义和正常路径缺少返回值。

安全清理 ​

bash
pwd
test ! -e out || rm -r -- out

确认位于本实验目录,只删除本章产物。

验收与清理 ​

给纯计算方法补上零和负数用例,并保留 Integer.MIN_VALUE 用例,数组实验能用“引用值副本”解释,四个重载调用都能说出选择阶段。栈轨迹要从异常暴露点一路还原到 main,三类故障则分别标成重复签名、调用歧义和缺少返回路径。保存记录后清理临时目录:

bash
cd
ls -ld -- "$chapter5_lab_root"
rm -r -- "$chapter5_lab_root"

方法与递归的路径都已验过,下一章把一组同类型值放进连续的索引空间。

→ 下一步:数组入门

Built with VitePress | Software Systems Atlas