跳到内容

第 1 卷 / 第 4 章 / 第 5 课

4.5 实验:分支、循环与边界 ​

前置知识:本章全部控制流结构

预计时间:50 分钟

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

完成标志:完成四项必做实验,并为每项列出边界案例

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

必做一:成绩分类边界 ​

保存为 GradeClassifier.java:

java
public class GradeClassifier {
    static String classify(int score) {
        if (score < 0 || score > 100) {
            return "invalid";
        } else if (score >= 90) {
            return "excellent";
        } else if (score >= 80) {
            return "good";
        } else if (score >= 60) {
            return "pass";
        }
        return "retry";
    }

    public static void main(String[] args) {
        int[] cases = {-1, 0, 59, 60, 79, 80, 89, 90, 100, 101};
        for (int score : cases) {
            System.out.println(score + ":" + classify(score));
        }
    }
}
bash
javac -d out GradeClassifier.java
java -cp out GradeClassifier

逐行核对分界点,不要只看程序是否成功退出。

必做二:switch 表达式 ​

保存为 ItemPrice.java:

java
public class ItemPrice {
    static int priceOf(String item) {
        if (item == null) {
            throw new IllegalArgumentException("item is null");
        }
        return switch (item) {
            case "book" -> 50;
            case "magazine" -> 40;
            case "notebook" -> 35;
            default -> throw new IllegalArgumentException("item: " + item);
        };
    }

    public static void main(String[] args) {
        System.out.println(priceOf("book"));
        System.out.println(priceOf("magazine"));
    }
}
bash
javac -d out ItemPrice.java
java -cp out ItemPrice

再分别传入 "hammer" 和 null,记录异常类型与信息。未知值不应悄悄变成价格 0。

必做三:九九乘法表 ​

保存为 MultiplicationTable.java:

java
public class MultiplicationTable {
    public static void main(String[] args) {
        for (int row = 1; row <= 9; row++) {
            for (int column = 1; column <= row; column++) {
                System.out.print(column + "×" + row + "="
                        + (column * row) + "\t");
            }
            System.out.println();
        }
    }
}
bash
javac -d out MultiplicationTable.java
java -cp out MultiplicationTable

验证:共 9 行;第 1 行 1 项;第 9 行 9 项;最后一项是 9×9=81。

必做四:寻找第一个目标 ​

保存为 GridSearch.java:

java
public class GridSearch {
    static int[] findFirst(int[][] grid, int target) {
        search:
        for (int row = 0; row < grid.length; row++) {
            for (int column = 0; column < grid[row].length; column++) {
                if (grid[row][column] == target) {
                    return new int[] {row, column};
                }
            }
        }
        return new int[] {-1, -1};
    }

    public static void main(String[] args) {
        int[][] grid = {{1, 2}, {3, 7}, {7, 9}};
        System.out.println(java.util.Arrays.toString(findFirst(grid, 7)));
        System.out.println(java.util.Arrays.toString(findFirst(grid, 8)));
    }
}
bash
javac -d out GridSearch.java
java -cp out GridSearch

预期为 [1, 1] 和 [-1, -1]:两个整数分别表示行、列,两个 -1 表示未找到。本例借用下一章的数组语法返回一对坐标,避免把坐标塞进 row * 1000 + column 后在列数较大时发生碰撞。约定外层数组和每行都不能为 null,空数组和空行则可以。生产代码可返回有明确字段的结果类型。

search: 标签没有被使用,故意保留让你观察:提取为方法并 return 后,标签已经多余,应删除。

选做一:可终止的猜数逻辑 ​

把随机数与交互拆开,先测试确定性核心:

java
static String compareGuess(int target, int guess) {
    if (guess < target) {
        return "low";
    }
    if (guess > target) {
        return "high";
    }
    return "correct";
}

测试 target=50 时的 49、50、51。真正交互循环还必须处理非数字、输入结束、最大尝试次数和取消,不要先写 while (true) 再假设用户总会猜中。

选做二:观察 continue 落点 ​

java
for (int value = 0; value < 5; value++) {
    if (value == 2) {
        continue;
    }
    System.out.println(value);
}

改写成等价 while,确保状态在 continue 之前推进。若程序卡住,先中断运行,再检查变量更新位置;不要让失控进程长期占用终端。

故障实验 ​

逐个打开:

java
// if (1) { }
// switch (1L) { default -> { } }
// for (int index = 0; index < 3; index++); { System.out.println("once"); }

前两条不能编译,第三条能编译但块只执行一次。记录“编译失败”和“语义错误”之间的区别。

安全清理 ​

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

确认当前为本实验目录,只删除本实验产物。

验收与清理 ​

逐项核对成绩分界点、未知商品与 null、乘法表行数和末项,以及搜索命中与未命中的结果。分别记录编译错误、运行时异常和能运行但行为不符的情况,保存记录后再清理:

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

分类、定价和搜索使用的语法不同,检查时都要落到具体路径:从哪里进入,状态怎样变化,在何处退出,异常交给谁。

下一章把重复逻辑封装起来。

→ 下一步:方法与调用栈

Built with VitePress | Software Systems Atlas