4.5 实验:分支、循环与边界
前置知识:本章全部控制流结构
预计时间:50 分钟
练习环境:JDK 17+ 与 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:
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));
}
}
}javac -d out GradeClassifier.java
java -cp out GradeClassifier逐行核对分界点,不要只看程序是否成功退出。
必做二:switch 表达式
保存为 ItemPrice.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"));
}
}javac -d out ItemPrice.java
java -cp out ItemPrice再分别传入 "hammer" 和 null,记录异常类型与信息。未知值不应悄悄变成价格 0。
必做三:九九乘法表
保存为 MultiplicationTable.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();
}
}
}javac -d out MultiplicationTable.java
java -cp out MultiplicationTable验证:共 9 行;第 1 行 1 项;第 9 行 9 项;最后一项是 9×9=81。
必做四:寻找第一个目标
保存为 GridSearch.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)));
}
}javac -d out GridSearch.java
java -cp out GridSearch预期为 [1, 1] 和 [-1, -1]:两个整数分别表示行、列,两个 -1 表示未找到。本例借用下一章的数组语法返回一对坐标,避免把坐标塞进 row * 1000 + column 后在列数较大时发生碰撞。约定外层数组和每行都不能为 null,空数组和空行则可以。生产代码可返回有明确字段的结果类型。
search: 标签没有被使用,故意保留让你观察:提取为方法并 return 后,标签已经多余,应删除。
选做一:可终止的猜数逻辑
把随机数与交互拆开,先测试确定性核心:
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 落点
for (int value = 0; value < 5; value++) {
if (value == 2) {
continue;
}
System.out.println(value);
}改写成等价 while,确保状态在 continue 之前推进。若程序卡住,先中断运行,再检查变量更新位置;不要让失控进程长期占用终端。
故障实验
逐个打开:
// if (1) { }
// switch (1L) { default -> { } }
// for (int index = 0; index < 3; index++); { System.out.println("once"); }前两条不能编译,第三条能编译但块只执行一次。记录“编译失败”和“语义错误”之间的区别。
安全清理
pwd
test ! -e out || rm -r -- out确认当前为本实验目录,只删除本实验产物。
验收与清理
逐项核对成绩分界点、未知商品与 null、乘法表行数和末项,以及搜索命中与未命中的结果。分别记录编译错误、运行时异常和能运行但行为不符的情况,保存记录后再清理:
cd
ls -ld -- "$chapter4_lab_root"
rm -r -- "$chapter4_lab_root"分类、定价和搜索使用的语法不同,检查时都要落到具体路径:从哪里进入,状态怎样变化,在何处退出,异常交给谁。
下一章把重复逻辑封装起来。