这是 Java 入门系列的第四天。顺序结构让程序从上往下走,选择结构让程序拐弯,今天要学的循环结构则让程序”重复”——这是计算机最擅长的事:不知疲倦地把一件事做一万次。

本篇目标

  • 掌握 for、while、do-while 的语义差异与选型原则
  • 理解嵌套循环”外行内列”的模型,能打印常见图案
  • 说清 break、continue、return 的真实语义和标号的用法
  • 完成两个经典练习:九九乘法表与素数判断

for:次数已知时的首选

for (int i = 1; i <= 10; i++) {
System.out.println("第 " + i + " 次 Hello");
}

执行顺序是四步循环:初始化(只执行一次)→ 判断条件(false 则结束)→ 执行循环体 → 执行迭代语句 → 回到判断。三个表达式把”从哪开始、到哪结束、每次怎么走”写在同一行,结构一目了然——这正是次数已知时优先用 for 的原因。

两个基础练习背后是两种重要的思想模式:

累加思想:求 1~100 的和,套路是”循环外定义累加器,循环内累加”。

int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}

统计思想:统计满足条件的个数,套路是”循环外定义计数器,循环内判断后自增”。

int count = 0;
for (int i = 100; i <= 999; i++) {
int ge = i % 10;
int shi = i / 10 % 10;
int bai = i / 100;
// 水仙花数:各位数字的立方和等于它本身,如 153 = 1³ + 5³ + 3³
if (ge * ge * ge + shi * shi * shi + bai * bai * bai == i) {
count++;
}
}

累加器和计数器是后面一切数据处理(求平均分、统计及格人数)的原型,务必练熟。

顺带提醒一个经典笔误:for (int i = 0; i < 10; i++);——括号后多写一个分号,循环体就变成了空语句,后面的大括号块只会执行一次。”有左大括号就没有分号,有分号就没有左大括号”,这句话值得记住。

while:条件驱动

int i = 1;               // 初始化
while (i <= 10) { // 判断条件
System.out.println(i);
i++; // 控制条件(千万别丢,否则死循环)
}

while 与 for 本质上等价(for 的三个部分拆开就是 while),差异在变量作用域与语义:for 的循环变量属于 for 内部,循环结束即销毁,省内存;while 的变量定义在循环外,循环结束后还能继续使用。语义上,while 表达的是”只要条件成立就一直做”,至于做多少次事先并不知道——比如”不断读取用户输入,直到输入 quit”。这种次数未知的场景,用 while 比 for 自然。

do-while:至少执行一次

do {
System.out.println("先做一次再说");
} while (false); // 注意这里有一个分号

它先执行循环体再判断条件,因此无论条件真假,循环体至少执行一次。适合”必须先做一遍再决定要不要继续”的场景,比如先打印一次菜单再等用户选择。使用频率远低于前两者,但语义必须清楚。

循环 判断时机 最少执行次数 典型场景
for 先判断 0 次数已知
while 先判断 0 次数未知、条件驱动
do-while 后判断 1 至少要做一次

死循环不是洪水猛兽

while (true) {} 与 for (;;) {} 是两种标准的死循环写法。它们并非总是 bug——服务器的主循环、游戏的渲染循环、命令行程序的”打印菜单 → 读输入 → 处理”循环,本质上都是刻意为之的死循环,配合 break 退出。真正要避免的是意外死循环:循环变量忘了更新、更新方向写反,程序就卡死了。

嵌套循环:外行内列

打印一个 4 行 5 列的星号矩阵:

for (int i = 0; i < 4; i++) {          // 外循环:行
for (int j = 0; j < 5; j++) { // 内循环:每行的列
System.out.print("*"); // print 不换行
}
System.out.println(); // 一行打完再换行
}

核心模型就一句话:外循环控制行数,内循环控制列数。让内循环的上限跟着外循环变量走,图案就活了——比如打印直角三角形:

for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= i; j++) { // 第 i 行打 i 个
System.out.print("*");
}
System.out.println();
}

实战一:九九乘法表

public class MultiplicationTable {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + "×" + i + "=" + (i * j) + "\t");
}
System.out.println();
}
}
}

"\t" 是制表符,把每个算式对齐到下一个制表位,输出就是整齐的三角阵。常见的转义字符还有 \n 换行、\r 回车、\" 双引号本身。

break、continue、return 与标号

三个跳转语句经常被混为一谈,其实层次完全不同:

  • break:跳出当前一层循环(或 switch),循环整体结束。
  • continue:跳过本次循环体的剩余部分,直接进入下一次迭代——循环并没有结束。
  • return:结束的是整个方法,循环只是顺带被终结,方法内后面的代码都不会执行。

嵌套循环里 break 只能跳一层,想直接跳出外层循环需要标号:

outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) {
break outer; // 直接结束外层循环
}
System.out.println(i + "," + j);
}
}

标号就是一个合法标识符加冒号。日常代码更推荐通过抽取方法来避免标号(return 天然能跳多层),但读老代码时要认得它。

练习:推理 break 与 continue 的输出

看一道经典填空题,检验是否真的理解了二者的差异:

for (int x = 1; x <= 10; x++) {
if (x % 3 == 0) {
// 在此处填写代码
}
System.out.println("Java");
}
  • 想输出 2 次:填 break;。x = 3 时满足条件直接跳出循环,只有 x = 1、2 两次打印。
  • 想输出 7 次:填 continue;。x = 3、6、9 三次被跳过,10 - 3 = 7 次打印。
  • 想输出 13 次:在 if 里加一句 System.out.println("Java");。正常打印 10 次,x = 3、6、9 时各多打一次,共 13 次。

把这道题想透,break”终结循环”和 continue”跳过本次”的区别就再也不会混了。

实战二:素数判断

素数是只能被 1 和自身整除的大于 1 的整数。朴素写法是试遍 2 到 n-1,但有个重要的优化:

public class PrimeDemo {
public static void main(String[] args) {
int n = 97;
boolean isPrime = n > 1;
for (int i = 2; i <= Math.sqrt(n); i++) { // 只需试到 √n
if (n % i == 0) {
isPrime = false;
break; // 找到因子即可收工
}
}
System.out.println(n + (isPrime ? " 是素数" : " 不是素数"));
}
}

为什么试到 √n 就够了?因为因子是成对出现的:若 n = a × b 且 a ≤ b,那么必有 a ≤ √n。如果 √n 以内都找不到因子,更大的一侧也不可能单独存在。这个优化把循环次数从 n 降到 √n——n 是一百万时,从约一百万次降到一千次,这就是算法思想的力量。

在单数判断外面再套一层循环,就得到 1~100 的全部素数:

public class PrimeList {
public static void main(String[] args) {
for (int n = 2; n <= 100; n++) {
boolean isPrime = true;
for (int i = 2; i <= Math.sqrt(n); i++) {
if (n % i == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
System.out.print(n + " ");
}
}
}
}

“内层解决一个问题,外层批量重复”——这是嵌套循环最典型的用法,和打印图案是同一个思维模式。

小结

  • 次数已知用 for,条件驱动用 while,至少执行一次才考虑 do-while。
  • 累加器与计数器是循环处理的两大基本模式。
  • 嵌套循环记牢”外行内列”;内层上限依赖外层变量,就能打印三角形和九九乘法表。
  • break 跳本层、continue 跳本次、return 结束方法;跨层跳转用标号,但更推荐抽方法。
  • 素数判断试到 √n 即可,找到因子立刻 break,别做无用功。