Skip to content
第 9 / 250 章后端⏱ 10 分钟阅读

第 9 章:流程控制 — 循环结构

学习目标

  • 掌握 for / while / do-while 三种循环
  • 学会 break、continue、带标签的 break
  • 理解 for-each 语法糖
  • 避免死循环和常见循环陷阱

一、为什么需要循环?

如果让你打印 1 到 100,你会怎么做?

java
// ❌ 反例:写 100 行
System.out.println(1);
System.out.println(2);
System.out.println(3);
// ... 一直写到 100

这就是循环要解决的问题:重复执行某段代码。

二、for 循环

2.1 标准 for 循环

java
for (初始化; 条件; 更新) {
    // 循环体
}
java
// 打印 1 到 5
for (int i = 1; i <= 5; i++) {  // ① i 从 1 开始,每次循环 +1,到 6 结束
    System.out.println(i);
}
// 输出:1 2 3 4 5

执行流程

2.2 多种写法

java
// 标准:从 1 到 10
for (int i = 1; i <= 10; i++) { }

// 反向:从 10 到 1
for (int i = 10; i >= 1; i--) { }

// 步长 2:打印偶数
for (int i = 0; i <= 10; i += 2) { }

// 多个变量
for (int i = 0, j = 10; i < j; i++, j--) {
    System.out.println("i=" + i + ", j=" + j);
}

2.3 for-each(增强 for)

JDK 5 引入的语法糖,专门用于遍历数组和集合:

java
int[] numbers = {1, 2, 3, 4, 5};

// ✅ 传统写法
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}

// ✅ for-each 写法(推荐)
for (int number : numbers) {  // ① ":" 读作 "in"
    System.out.println(number);
}

for-each 的限制

java
// ❌ 不能修改原数组
for (int number : numbers) {
    number = number * 2;  // 这只是修改临时变量,不影响原数组
}

// ❌ 不能访问索引
for (int number : numbers) {
    // 不知道当前是第几个
}

// ✅ 需要索引时用传统 for
for (int i = 0; i < numbers.length; i++) {
    if (i % 2 == 0) {
        System.out.println("索引 " + i + ": " + numbers[i]);
    }
}

三、while 循环

当循环次数不固定时,用 while 更合适。

java
while (条件) {
    // 循环体
}
java
// 场景:用户输入直到正确为止
Scanner scanner = new Scanner(System.in);
String input = "";

while (!"exit".equals(input)) {  // ① 条件为 true 时持续执行
    System.out.print("输入命令(exit 退出): ");
    input = scanner.nextLine();
    System.out.println("收到: " + input);
}

while vs for 选择

java
// 已知次数:用 for
for (int i = 0; i < 100; i++) {
    // ...
}

// 未知次数:用 while
while (!condition) {
    // ...
}

四、do-while 循环

先执行,再判断。至少执行一次。

java
do {
    // 循环体
} while (条件);  // 注意分号
java
// 场景:菜单选择,至少要显示一次
int choice;
do {
    System.out.println("1. 开始游戏");
    System.out.println("2. 加载存档");
    System.out.println("3. 退出");
    System.out.print("请选择: ");
    choice = scanner.nextInt();
} while (choice < 1 || choice > 3);

五、break 和 continue

5.1 break:跳出循环

java
// 找到第一个满足条件的就退出
for (int i = 1; i <= 100; i++) {
    if (i % 17 == 0) {
        System.out.println("找到第一个 17 的倍数: " + i);
        break;  // ① 跳出整个 for 循环
    }
}

5.2 continue:跳过本轮

java
// 跳过偶数,只处理奇数
for (int i = 1; i <= 10; i++) {
    if (i % 2 == 0) {
        continue;  // ① 跳过本轮,进入下一轮
    }
    System.out.println(i);
}
// 输出:1 3 5 7 9

5.3 带标签的 break(跳出多层循环)

java
// 场景:在二维数组中查找,找到后跳出所有循环
int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

boolean found = false;
outer:                            // ① 标签
for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        if (matrix[i][j] == 5) {
            System.out.println("找到 5 在 [" + i + "][" + j + "]");
            found = true;
            break outer;           // ② 跳出 outer 标签标记的循环
        }
    }
}

带标签的 break 谨慎使用

  1. 可读性差,新人难理解
  2. 大部分情况可以用方法 + return 替代
  3. 仅在确实需要跳出多层循环时使用

六、循环的常见陷阱

6.1 死循环

java
// ❌ 死循环 1:忘记更新循环变量
int i = 0;
while (i < 10) {
    System.out.println(i);
    // 忘记 i++
}

// ❌ 死循环 2:条件永远为真
for (;;) {  // 等价于 while (true)
    // 没有 break 就会无限执行
}

6.2 浮点数循环的精度问题

java
// ❌ 危险:浮点数累加可能导致循环次数错误
for (double d = 0.0; d != 1.0; d += 0.1) {
    System.out.println(d);
    // 可能少输出 1.0(因为 0.1 不能精确表示)
}

// ✅ 改用整数计数
for (int i = 0; i <= 10; i++) {
    double d = i / 10.0;
    System.out.println(d);
}

6.3 集合遍历时修改

java
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));

// ❌ ConcurrentModificationException
for (Integer num : list) {
    if (num == 2) {
        list.remove(num);  // 遍历时删除会抛异常
    }
}

// ✅ 用迭代器
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
    if (it.next() == 2) {
        it.remove();  // 迭代器的 remove 是安全的
    }
}

七、完整代码示例

java
/**
 * 循环结构综合示例
 *
 * 配套文档:第 9 章 流程控制 - 循环结构
 * 运行方式:javac LoopDemo.java && java LoopDemo
 */
public class LoopDemo {
    public static void main(String[] args) {

        // ============ 1. for 循环 ============
        System.out.println("=== for ===");
        for (int i = 1; i <= 5; i++) {
            System.out.print(i + " ");
        }
        System.out.println();

        // ============ 2. for-each 遍历 ============
        System.out.println("\n=== for-each ===");
        int[] scores = {85, 92, 78, 95, 88};
        int total = 0;
        for (int score : scores) {
            total += score;
        }
        System.out.println("总分: " + total + ", 平均: " + total / scores.length);

        // ============ 3. while 循环(未知次数)============
        System.out.println("\n=== while ===");
        int sum = 0, n = 1;
        while (sum < 100) {
            sum += n;
            n++;
        }
        System.out.println("累加到 " + sum + " 用了 " + (n - 1) + " 个数");

        // ============ 4. do-while(至少执行一次)============
        System.out.println("\n=== do-while ===");
        int i = 10;
        do {
            System.out.println("至少执行一次: i = " + i);
        } while (i < 5);  // 条件为 false,但仍执行了一次

        // ============ 5. break 和 continue ============
        System.out.println("\n=== break 和 continue ===");
        for (int j = 1; j <= 10; j++) {
            if (j == 3) continue;       // ① 跳过 3
            if (j == 7) break;          // ② 到 7 退出
            System.out.print(j + " ");
        }
        System.out.println();

        // ============ 6. 带标签的 break ============
        System.out.println("\n=== 带标签的 break ===");
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
        searchLabel:
        for (int row = 0; row < matrix.length; row++) {
            for (int col = 0; col < matrix[row].length; col++) {
                if (matrix[row][col] == 5) {
                    System.out.println("找到 5: [" + row + "][" + col + "]");
                    break searchLabel;
                }
            }
        }

        // ============ 7. 嵌套循环:九九乘法表 ============
        System.out.println("\n=== 九九乘法表 ===");
        for (int a = 1; a <= 9; a++) {
            for (int b = 1; b <= a; b++) {
                System.out.print(b + "×" + a + "=" + (a * b) + "\t");
            }
            System.out.println();
        }
    }
}

运行结果

=== for ===
1 2 3 4 5

=== for-each ===
总分: 438, 平均: 87

=== while ===
累加到 105 用了 14 个数

=== do-while ===
至少执行一次: i = 10

=== break 和 continue ===
1 2 4 5 6

=== 带标签的 break ===
找到 5: [1][1]

=== 九九乘法表 ===
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
...

八、常见面试题

Q1: 三种循环的使用场景?

循环适用
for已知循环次数
while未知次数,靠条件退出
do-while至少执行一次(很少用)
for-each遍历数组 / 集合(推荐)

Q2: for 和 while 性能有差异吗?

现代 JVM 优化后无差异。for 循环的初始化只执行一次,while 也类似。选择依据是可读性,不是性能。

Q3: break 和 continue 的区别?

  • break:跳出整个循环
  • continue:跳过本轮,进入下一轮

Q4: for(;😉 和 while(true) 一样吗?

完全等价。for(;;) 没有初始 / 条件 / 更新,等价于无限循环。

九、本章小结

要点关键
for 循环已知次数,三段式(初始化/条件/更新)
for-each遍历数组 / 集合的首选,但不能改原集合
while未知次数,靠条件退出
do-while至少执行一次
break跳出整个循环
continue跳过本轮,进入下一轮
带标签的 break跳出多层循环,谨慎使用

动手练习

练习 1:基础题

打印 1 到 100 中所有 3 的倍数:

java
for (int i = 1; i <= 100; i++) {
    // 你的代码
}
// 期望输出:3 6 9 12 ... 99

练习 2:进阶题

打印斐波那契数列前 20 项:

1 1 2 3 5 8 13 21 34 ...

练习 3:挑战题

找 100 以内的所有素数(只能被 1 和自己整除):

java
// 提示:外层循环 2~100,内层循环判断能否被整除
// 期望输出:2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 ...

推荐阅读


下一章第 10 章:方法定义、调用与重载

本站基于 VitePress 构建 · 由 Codebook 团队维护