跳到内容

第 1 卷 / 第 4 章 / 第 3 课

4.3 while、for 与遍历 ​

前置知识:布尔条件、作用域和赋值

预计时间:35 分钟

完成标志:能按条件、计数或元素遍历选择循环,并说明每一步顺序

while:先判断,再执行 ​

java
int delivered = 0;

while (delivered < 10) {
    delivered++;
    System.out.println("交付第 " + delivered + " 块零件");
}

每轮顺序:

text
判断条件
  ├─ false:结束循环
  └─ true:执行循环体 → 回到条件

循环正确性至少要回答:

  • 初始状态是什么?
  • 每轮如何推进?
  • 终止条件何时变成 false?
  • 边界轮次是否多一次或少一次?

条件一开始就是 false 时,循环体一次也不执行。

for:把计数结构写在一处 ​

java
for (int index = 0; index < 10; index++) {
    System.out.println(index);
}

执行顺序:

text
初始化(一次)
  ↓
条件
  ├─ false:结束
  └─ true:循环体 → 更新 → 回到条件

这解释了一个细节:在 for 中执行 continue 后,会先执行更新表达式,再检查条件;在 while 中,continue 直接回到条件,因此你必须确保状态已经推进。

for 的三部分都可以省略:

java
for (;;) {
    // 显式无限循环
}

无限循环本身不一定是错误。事件循环、服务器和消费者会长期运行,但必须设计取消、关闭、异常、退避和资源释放路径。教程练习通常使用最大轮数,避免失控。

半开区间减少边界错误 ​

数组索引通常采用 [0, length):

java
int[] scores = {80, 90, 70};

for (int index = 0; index < scores.length; index++) {
    System.out.println(index + ": " + scores[index]);
}

初始索引包含 0,结束边界不包含 length。最后一个合法索引是 length - 1。

反向遍历:

java
for (int index = scores.length - 1; index >= 0; index--) {
    System.out.println(scores[index]);
}

空数组时初始值为 -1,条件立即为 false。

增强 for:按元素读取 ​

java
for (int score : scores) {
    System.out.println(score);
}

适合不需要索引的遍历。循环变量接收每个元素值;给循环变量重新赋值不会改回基本类型数组:

java
for (int score : scores) {
    score = 0;
}
System.out.println(scores[0]); // 仍是 80

对对象数组,循环变量复制的是引用值;可以通过引用修改对象状态,但给循环变量指向另一个对象也不会替换数组元素。后续集合章节再讨论迭代期间结构修改。

do-while:先执行,再判断 ​

java
int attempts = 0;

do {
    attempts++;
    System.out.println("attempt " + attempts);
} while (attempts < 1);

循环体至少执行一次。末尾 while (...) 后必须有分号。它适合“先读取一次,再判断是否继续”的流程,但交互和资源代码还要处理输入结束、异常与取消。

不用浮点等值控制轮数 ​

java
double value = 0.0;
while (value != 1.0) {
    value += 0.1;
}

二进制近似可能跳过精确的 1.0,使条件一直为 true。按次数迭代更可靠:

java
for (int step = 0; step < 10; step++) {
    double value = step / 10.0;
    System.out.println(value);
}

若循环由物理误差阈值控制,应使用领域定义的容差并加入最大迭代次数,避免只靠一个近似条件保证终止。

常见循环错误 ​

状态不推进:

java
int count = 0;
while (count < 10) {
    System.out.println(count);
    // 忘了更新 count
}

分号形成空循环体:

java
for (int index = 0; index < 10; index++); // 空语句
{
    System.out.println("只执行一次");
}

格式化工具可能让结构更明显,但最终需要代码评审和测试覆盖 0 次、1 次、最后一次与超界条件。

Python 与 C++ 边界 ​

Python for 遍历可迭代对象,range(10) 提供 0 到 9;它不是 C 风格三段式 for。Python 还支持循环 else:for 正常耗尽元素或 while 条件变为假时执行;break、return 或异常离开循环时不会执行。

C++ 有相似的 while、do-while、三段式和范围 for,但条件可发生到 bool 的上下文转换,类型、迭代器失效和未定义行为边界也不同,不能称为完全相同。

专业速查 ​

情况首选结构
已知计数或索引for
由状态决定继续while
至少执行一次do-while
只读取每个元素增强 for
长期运行服务明确取消、退避、异常和资源关闭
数值迭代误差条件 + 最大迭代次数

离开这一页前 ​

从空数组、单元素数组和三个元素的数组各走一遍 for,标出初始化、条件、循环体和更新发生的顺序。然后说明 continue 在 for 与 while 中分别落到哪里,以及为什么近似数值循环还需要最大迭代次数。能画出这几条路径,循环边界才算真正可检查。

下一页处理两种常见需求:找到目标后停止,遇到无效项时跳过本轮。

→ 下一步:break、continue 与标签

规范入口:JLS §14.12–14.14:while、do 与 for。

Built with VitePress | Software Systems Atlas