← Java 基础教程

第 10 / 40 课 · 免费教程

挖到钻石才停

while、do-while 与工具耐久模拟器

90–110 分钟Java 11+入门 · while 循环

本课作品

制作工具耐久模拟器

不知道要挖几块,也要知道怎样停。读懂前置与后置检查,给搜索加上次数护栏,用固定随机种子重放结果,完成有停止原因的耐久模拟器。

开始前:建议先完成第 1–9 课

01 / 14

钻石没出现,镐子先打了喷嚏

史蒂夫宣布再挖7块。第7块后面只有圆石,于是他改成70块。镐子听完,耐久条先晕了过去:目标在哪里,事先不知道;可工具能坚持多久,不能不管。

把任务说成“只要还没找到目标,而且没有超过上限,就继续尝试”。while 会反复检查这句条件。终点未知,不等于没有停止规则。

  • while 为什么可能一轮也不执行?
  • 循环中哪个状态必须变化,才会靠近出口?
  • 随机消耗怎样重复测试,怎样提前安全停机?

02 / 14

while:先过门,再动手

每轮开始前,while 先检查 boolean 条件。true 才进入循环体,执行完再回到条件。第一轮前已经为false,循环体就执行0次。

片段:放在 main 中,单独运行

int durability = 3;
while (durability > 0) {
    System.out.println("挖一下,动作前耐久=" + durability);
    durability--;
}
System.out.println("镐子坏了");

运行结果

挖一下,动作前耐久=3
挖一下,动作前耐久=2
挖一下,动作前耐久=1
镐子坏了
先检查,动作和更新后再回去
步骤操作
1 · 只执行一次durability = 3
2 · 检查条件durability > 0 ?
3 · true 时执行模拟挖一块
4 · 更新,回到步骤 2durability--

条件为 false 时退出循环。

03 / 14

让状态向停止条件移动

片段:放在 main 中,单独运行

int durability = 0;
while (durability > 0) {
    System.out.println("这一行不会出现");
}
System.out.println("先修工具");

运行结果

先修工具

每轮不一定只消耗1点。消耗3点时,耐久8会经过5、2,然后跨过0。Math.max(0, 新值) 将显示值夹到0,不让耐久条出现负数。

片段:放在 main 中,单独运行

int durability = 8;
while (durability > 0) {
    durability = Math.max(0, durability - 3);
    System.out.println("剩余耐久=" + durability);
}

运行结果

剩余耐久=5
剩余耐久=2
剩余耐久=0
追踪表:写清动作前后
轮次动作前直接减3显示的新耐久
1855
2522
32-10
下一次0条件为false不再挖掘

04 / 14

找不到目标,也要有安全出口

先用确定位置研究搜索,避免随机结果干扰理解。设第7块有钻石:每挖一次,块数加1,再判断是不是目标。最多20块,两项继续条件必须同时满足。

片段:放在 main 中,单独运行

int blocks = 0, diamondAt = 7;
boolean found = false;
while (!found && blocks < 20) {
    blocks++;
    found = blocks == diamondAt;
}
System.out.println("挖了" + blocks + "块,找到=" + found);

运行结果

挖了7块,找到=true
两道护栏:任意一项失败就停止
  1. !found还没找到,才需要继续搜索。
  2. blocks < 20仍在次数上限内,才允许继续。
  3. 用 && 连接成功或到上限,都会离开循环。
一次只改 diamondAt
目标位置实际块数停止结果
11找到=true
77找到=true
2020找到=true
3020找到=false,到上限

05 / 14

do-while:先做一次,再检查

把条件放在末尾,循环体就至少执行一次。适合必须先展示的菜单或先发生的一次尝试。最后的 while (条件); 有一个必需的分号。

片段:放在 main 中,单独运行

int choice = 3;
do {
    System.out.println("1 挖矿  2 修理  3 退出");
} while (choice != 3);

运行结果

1 挖矿  2 修理  3 退出
至少一轮:先执行,再检查
  1. 执行循环体首次直接执行
  2. 再检查条件true → 回到循环体;false → 退出

片段:放在 main 中,单独运行

int durability = 0;
do {
    durability += 3;
    System.out.println("修理后=" + durability);
} while (durability < 9);

运行结果

修理后=3
修理后=6
修理后=9

06 / 14

先检查,还是先动作?

差别在第一次检查的时机
  1. while先检查再做,允许0轮。工具已损坏时,不应继续挖。
  2. do-while先做再检查,至少1轮。退出菜单也要先让玩家看见。

片段:放在 main 中,单独运行

int torches = 0;
while (torches > 0) {
    System.out.println("放火把");
    torches--;
}
System.out.println("结束");

运行结果

结束

片段:放在 main 中,单独运行

int items = 0;
do {
    System.out.println("检查箱子");
} while (items > 0);

运行结果

检查箱子

不是哪一种更高级,而是第一轮动作应不应该先发生。别为了保证“至少一次”,把已经损坏的工具交给 do-while 强行挖一下。

07 / 14

Random:让变化可以重放

伪随机可以模拟不同消耗。测试时使用固定种子10,相同种子加上相同调用顺序,会得到相同序列。这样遇到 Bug,能重放同一组消耗。

片段:放在 main 中,单独运行

java.util.Random random = new java.util.Random(10);
int cost = 1 + random.nextInt(4);
System.out.println(cost);

运行结果

3

片段:放在 main 中,单独运行

java.util.Random random = new java.util.Random(10);
int count = 0;
while (count < 5) {
    int cost = 1 + random.nextInt(4);
    System.out.println(cost);
    count++;
}

运行结果

3
2
2
2
1
随机范围:上界不包含
表达式可能结果
random.nextInt(4)0、1、2、3
1 + random.nextInt(4)1、2、3、4
2 + random.nextInt(4)2、3、4、5

08 / 14

停不下来的循环急诊室

读代码查原因,不运行失控反例

  • 条件变量没有更新:durability 保持5,durability > 0 就一直成立。
  • while 后多了分号:循环体变成空语句,后面的更新根本进不去。
  • && 写成 ||:只要有一项为true就继续,成功和上限都失去独立停机作用。
  • 安全上限计数没有更新:看似写了上限,实际永远到不了它。

片段:放在 main 中,单独运行

int blocks = 0;
while (blocks < 3) {
    blocks++;
    System.out.println("已挖" + blocks + "块");
}

运行结果

已挖1块
已挖2块
已挖3块

09 / 14

预测变化,也预测停止原因

把循环的出口一起写进答案
  1. 预测列出每轮开始与结束的状态。
  2. 运行核对实际序列,不只看最后一行。
  3. 解释指出哪一项继续条件先失效。

片段:放在 main 中,单独运行

int durability = 7;
while (durability > 0) {
    durability = Math.max(0, durability - 2);
    System.out.println(durability);
}

片段:放在 main 中,单独运行

int blocks = 0;
boolean found = false;
while (!found && blocks < 3) {
    blocks++;
}
System.out.println(blocks + ":" + found);

片段:放在 main 中,单独运行

int energy = 0;
do {
    System.out.println("检查");
} while (energy > 0);

核对:第一题逐行输出5、3、1、0,耐久归零;第二题输出3:false,次数上限先到;第三题输出一次“检查”,随后条件为false。

10 / 14

项目蓝图:工具耐久模拟器

用24点耐久模拟挖掘,每轮随机消耗1到4点,最多50块。打印轮次、消耗、剩余耐久和耐久条,再明确说明为什么停止。

每次随机消耗之后,两项状态都更新
步骤操作
1 · 只执行一次耐久24,块数0
2 · 检查条件耐久>0 且未到上限
3 · true 时执行抽取消耗
4 · 更新,回到步骤 2更新状态并打印

条件为 false 时退出循环。

先写契约,再写代码
项目本课规则
允许设定耐久和上限均为0到100的整数
随机循环外 new Random(10),每轮抽取一次
显示Math.max 保证耐久不低于0
耐久条每2点显示一个 #,只作简易比例提示
结束耐久为0报告损坏,否则报告安全上限

11 / 14

模拟器代码:初始化与消耗

新建 DurabilitySimulator.java。完整程序分两页,依次连在同一文件内。每轮只抽取一次消耗,方便和固定序列逐项对照。

DurabilitySimulator.java · 上半部分,下一页继续

import java.util.Random;

public class DurabilitySimulator {
    public static void main(String[] args) {
        final int START_DURABILITY = 24;
        final int SAFETY_LIMIT = 50;
        int durability = START_DURABILITY, blocksMined = 0;
        Random random = new Random(10);
        if (START_DURABILITY < 0 || START_DURABILITY > 100
                || SAFETY_LIMIT < 0 || SAFETY_LIMIT > 100) {
            System.out.println("耐久和上限必须在0到100之间");
        } else {
            System.out.println("=== 工具耐久模拟 ===");
            while (durability > 0 && blocksMined < SAFETY_LIMIT) {
                int cost = 1 + random.nextInt(4);
                blocksMined++;
                durability = Math.max(0, durability - cost);
                String bar = "#".repeat(durability / 2);
                System.out.printf("第%02d块 消耗%d 剩余%02d %s%n",
                        blocksMined, cost, durability, bar);
            }

12 / 14

模拟器收尾:说清停机原因

DurabilitySimulator.java · 紧接上一页

            if (durability == 0) {
                System.out.println("工具损坏,停止挖掘");
            } else {
                System.out.println("触发安全上限,带着工具回家");
            }
            System.out.println("共挖掘:" + blocksMined + "块");
        }
    }
}

两项条件都可能令循环结束。如果最后耐久刚好归零,同时也达到上限,本项目优先报告工具损坏。先把提示优先级写清楚,就不会把结果交给运气决定。

标准版24点耐久,前五轮可手算
块数本次消耗剩余耐久耐久条长度
132110
22199
32178
42157
51147

13 / 14

验收:证明两扇出口都能用

只改一个常量,观察不同出口
测试设定块数剩余耐久停止原因
耐久0,上限5000工具损坏
耐久1,上限5010工具损坏
耐久24,上限50110工具损坏
耐久24,上限3317安全上限
耐久24,上限0024安全上限
耐久1,上限110优先报告损坏
耐久-1或上限101不挖掘不统计报告范围错误

验证的是规则,不是“看起来没崩”

  • 每轮的消耗都在1到4之间,剩余耐久永不为负。
  • 总块数等于实际打印的挖掘记录数。
  • 上限3时,恰好打印3条,不多挖第4块。
  • 标准版重复运行,所有行一致;更换种子后也守住范围和上限。

14 / 14

给未知终点装上可靠护栏

木镐任务:从十倒数到零

只用 while 输出10到0,再打印“发射”。共11个数字;另测起点0和1,解释为什么 >= 0 才能包含最后的0。

铁镐任务:饥饿值下降模拟

初始20,每轮随机下降2到5,固定种子10,显示值夹到0。打印轮次、消耗与剩余值,并设安全上限;改成上限2时必须提前停止。

钻石镐任务:可以重复的菜单

Scanner 配合 do-while:1挖矿、2修理、3退出。挖矿前查耐久,修理最多到20;未知输入提示重试。用字符串选择可避免数字解析错误,菜单须至少显示一次。

技术核对:Oracle Java 语言规范第14章;java.util.Random API。
https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Random.html

TAKE IT WITH YOU

下载本课 PDF

完整课文、代码、图解与练习,选择你需要的语言。