摘要: 本文系统讲解了 Java 中循环结构的核心概念与使用方法,涵盖 while、do-while、for 及增强 for 循环的语法、执行流程和适用场景,并深入介绍了 break、continue 控制语句、循环嵌套以及常见陷阱。通过丰富的代码示例和综合的猜数字游戏实战,帮助初学者快速掌握循环这一实现代码重复执行的关键技术,为后续学习打下坚实基础。
关键词: Java 循环, while 循环, for 循环, break 和 continue, 循环嵌套
我们学会了变量来存储数据,学会了运算符来处理数据,还学会了条件判断来让程序做决策。但你会发现,如果程序只会从上到下执行一次,那么很多事情都做不了。
比如,你想打印 100 遍“我爱你”,难道要写 100 行 System.out.println("我爱你"); 吗?当然不是。如果让你计算 1 到 100 的总和,难道要写 1+2+3+…+100 吗?那样既不现实,也毫无意义。
我们需要一种能力:让一段代码重复执行多次,直到某个条件不再满足为止。这就是循环(Loop)。
循环是程序设计中最重要的控制结构之一,它让程序具备了“批量处理”的能力。Java 提供了三种循环结构:while、do-while 和 for。另外,还有一种专门遍历集合的“增强 for 循环”(for-each)。我们来逐一认识它们。
1. while 循环 —— 先判断,再执行
while 循环是最基本的一种。它的语法是:
while (条件) {
// 循环体:当条件为 true 时,重复执行这里的代码
}
执行流程很简单:
如果条件一开始就是 false,循环体一次都不会执行。
看一个经典例子:打印 1 到 5 的数字。
int i = 1; // 初始化计数器
while (i <= 5) { // 条件:i 小于等于 5
System.out.println(i);
i++; // 更新计数器,否则会死循环
}
System.out.println("循环结束!");
输出:
1
2
3
4
5
循环结束!
这里我们用了三个要素:
- 初始化:int i = 1;
- 条件:i <= 5
- 更新:i++
缺少任何一个,循环都可能出问题。尤其是如果忘了 i++,条件永远为真,程序会一直打印 1,直到你强行终止它——这就是死循环(无限循环)。写循环时一定要确保条件最终会变为 false。
while 常用于“不确定次数”的循环
比如,你让用户输入一个正数,如果输入的不是正数,就一直提示重新输入。你事先不知道用户会输错多少次,但循环可以一直问下去。
Scanner scanner = new Scanner(System.in);
int number;
System.out.print("请输入一个正数:");
number = scanner.nextInt();
while (number <= 0) {
System.out.print("输入无效,请重新输入一个正数:");
number = scanner.nextInt();
}
System.out.println("你输入了:" + number);
2. do-while 循环 —— 先执行,再判断
do-while 和 while 非常相似,唯一的不同是:do-while 先执行一次循环体,然后再判断条件。所以至少会执行一次。
语法:
do {
// 循环体
} while (条件);
注意最后有一个分号 ;,别漏掉。
把上面的“输入正数”的例子用 do-while 改写,会更自然,因为我们至少需要询问一次。
Scanner scanner = new Scanner(System.in);
int number;
do {
System.out.print("请输入一个正数:");
number = scanner.nextInt();
} while (number <= 0);
System.out.println("你输入了:" + number);
这样写,代码更紧凑,不需要在循环前重复写一次输入语句了。
什么时候用 while,什么时候用 do-while?
- 如果可能一次都不执行,用 while。
- 如果至少需要执行一次(比如菜单交互、输入验证),用 do-while。
3. for 循环 —— 计数循环的“集大成者”
for 循环是最常用的循环结构,尤其适合已知循环次数的场景。它把“初始化”、“条件”、“更新”三个要素整齐地写在一行,结构清晰。
语法:
for (初始化; 条件; 更新) {
// 循环体
}
执行顺序:
看一个打印 1 到 5 的例子:
for (int i = 1; i <= 5; i++) {
System.out.println(i);
}
输出一样。这里 int i = 1 只在循环开始时执行一次,i++ 在每次循环体执行完后执行。变量 i 的作用域仅限于 for 循环内部,循环结束后 i 就不可用了(除非你在外部提前定义)。
更复杂的 for 用法
- 可以初始化多个变量:用逗号分隔,但类型必须相同。
- 更新部分也可以多条语句:用逗号分隔。
- 三个部分都可以省略,但分号不能省。比如 for (;;) 就是一个死循环(相当于 while(true))。
// 同时初始化 i 和 j
for (int i = 0, j = 10; i < j; i++, j—) {
System.out.println("i=" + i + ", j=" + j);
}
// 输出:i=0, j=10; i=1, j=9; i=2, j=8; i=3, j=7; i=4, j=6
计算 1 到 100 的总和
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i; // sum = sum + i
}
System.out.println("1到100的总和是:" + sum); // 5050
4. 增强 for 循环(for-each) —— 专为遍历而生
在 JDK 5 中引入了一种更简洁的循环形式,专门用来遍历数组或集合(我们后面会详细学)。它不需要索引,也不需要手动维护计数器,语法简单:
for (元素类型 变量名 : 数组或集合) {
// 循环体
}
比如遍历一个整数数组:
int[] numbers = {10, 20, 30, 40, 50};
for (int num : numbers) {
System.out.println(num);
}
它会依次把数组中的每个元素赋值给 num,然后执行循环体。你不用关心索引,也不用担心越界。
不过,增强 for 循环不能修改数组/集合的元素(因为它拿到的只是一个副本),而且在需要索引的情况下(比如要修改某个位置的值)也不适用,此时还得用普通的 for。
我们在第四阶段学习集合时会大量使用增强 for,现在先留个印象。
5. break 和 continue —— 跳出或跳过
在循环体中,有时我们需要提前结束循环,或者跳过当前这次迭代,进入下一次。这时就需要 break 和 continue。
- break:立即终止整个循环,跳出循环体,执行循环后面的代码。
- continue:立即结束本次迭代,跳过本轮循环体中剩余的代码,直接进入下一次迭代(对于 for 循环,会先执行更新部分,再判断条件)。
// break 示例:找到第一个能被 7 整除的数
for (int i = 1; i <= 100; i++) {
if (i % 7 == 0) {
System.out.println("第一个能被7整除的数是:" + i);
break; // 找到就跳出循环
}
}
// 输出 7
// continue 示例:打印 1 到 10 之间的奇数
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // 偶数跳过,不打印
}
System.out.println(i);
}
// 输出 1,3,5,7,9
带标签的 break 和 continue(了解即可)
在嵌套循环中,break 默认只跳出当前最内层循环。如果你想从内层循环直接跳出外层循环,可以使用标签(label):
outer: // 标签,后面跟冒号
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break outer; // 直接跳出外层循环
}
System.out.println("i=" + i + ", j=" + j);
}
}
// 只会打印 i=0,j=0; i=0,j=1; i=0,j=2; i=1,j=0
虽然带标签的 break 有用,但过多的嵌套和跳转会降低代码可读性。一般尽量通过设计循环条件来避免复杂跳转。
6. 循环嵌套 —— 循环里面套循环
一个循环的循环体内包含另一个循环,这叫嵌套循环。最常见的场景是打印二维图形、处理矩阵等。
比如打印一个 5 行 5 列的星号矩形:
for (int row = 1; row <= 5; row++) {
for (int col = 1; col <= 5; col++) {
System.out.print("* ");
}
System.out.println(); // 每行结束换行
}
输出:
* * * * *
* * * * *
* * * * *
* * * * *
* * * * *
稍微改一下,打印一个直角三角形:
for (int row = 1; row <= 5; row++) {
for (int col = 1; col <= row; col++) {
System.out.print("* ");
}
System.out.println();
}
输出:
*
* *
* * *
* * * *
* * * * *
嵌套循环时,外层循环控制“行数”或“轮数”,内层循环控制“每行的细节”。理解这个层次关系是关键。
7. 循环中的常见陷阱
- 死循环:条件永远为真。比如 while (true) 或 for (;;),或者忘了更新变量。一定要确保循环能结束。
- 差一错误(Off-by-one):循环条件中的 < 和 <= 搞混,导致多一次或少一次。比如要循环 5 次,用 i < 5(0~4)还是 i <= 5(0~5)?要清楚。
- 在循环内改变循环变量的值:如果不小心在循环体里修改了循环变量,可能导致意外行为。
- break 和 continue 的滥用:它们会让代码逻辑变得不那么直观,尽量用循环条件本身来控制流程,而不是依赖跳转。
8. 综合示例 —— 猜数字游戏
我们把今天学的循环和条件判断结合起来,写一个有趣的“猜数字”游戏:程序随机生成一个 1~100 的整数,用户输入猜测,程序提示“大了”或“小了”,直到猜中为止,并统计猜的次数。
package com.example.demo;
import java.util.Scanner;
import java.util.Random; // Random 类用于生成随机数
/**
* 猜数字游戏:演示 while 循环、break、条件判断
*/
public class GuessNumber {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Random random = new Random();
// 生成 1~100 的随机整数
int target = random.nextInt(100) + 1; // nextInt(100) 返回 0~99,+1 变成 1~100
int guess;
int attempts = 0;
boolean guessed = false;
System.out.println("===== 猜数字游戏 =====");
System.out.println("我已经想好了一个 1~100 之间的整数,猜猜看吧!");
// 使用 while 循环,直到猜对为止
while (!guessed) {
System.out.print("请输入你的猜测:");
guess = scanner.nextInt();
attempts++;
if (guess < target) {
System.out.println("太小了,再大一点!");
} else if (guess > target) {
System.out.println("太大了,再小一点!");
} else {
System.out.println("恭喜你,猜对了!答案就是 " + target);
System.out.println("你一共猜了 " + attempts + " 次。");
guessed = true; // 设置标志,退出循环
// 也可以用 break,但用标志让代码更清晰
}
}
scanner.close();
}
}
这个程序综合了我们学过的变量、运算符、条件判断和循环,是一个很好的阶段性练习。
9. 循环的选择指南
| 已知确切的循环次数(如遍历数组、计数) | for |
| 根据条件决定是否继续,可能一次都不执行 | while |
| 至少需要执行一次(如输入验证、菜单) | do-while |
| 遍历数组或集合的所有元素 | 增强 for(for-each) |
今天的总结
今天我们解锁了程序的“批量处理”能力:
- while:先判断后执行,可能为 0 次。
- do-while:先执行后判断,至少为 1 次。
- for:集初始化、条件、更新于一体,适合计数循环。
- 增强 for:简洁遍历数组/集合。
- break 和 continue:控制循环的提前退出或跳过。
- 嵌套循环:处理多维问题。
循环是程序中最常见的结构之一,几乎每个有用的程序都离不开它。掌握好循环,你就能写出更加强大、灵活的程序。
动手试试
循环非常强大,但也很容易写出 bug。多练习几次,你就能驾驭它了。
我们下一篇会讲方法——如何把代码组织成可复用的“积木块”,让你的程序更有条理。
我们下一篇见。😃
📌 获取本系列示例代码请访问 GitCode。