break、continue 与标签
本教程共 100 篇 · 第 23 篇 · 更新于 2026-08-05 · 约 15 分钟阅读
23. break、continue 与标签
本节目标:搞懂 break 怎么立刻跳出循环、continue 怎么跳过本轮,以及带标签的 break 如何在嵌套循环里精准跳出指定层。
循环跑着跑着,常有「到这儿不用再继续了」或「这轮不要了,直接下一轮」的需求。break 和 continue 就是干这个的。
没有它们会怎样
先感受一下缺了 break 的日子。在一堆数里找第一个能被 7 整除的数:
int[] nums = {3, 8, 14, 21, 35, 42};
int found = -1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] % 7 == 0 && found == -1) { // 还得加个「没找到过」的判断
found = nums[i];
}
}
```java
数组有 6 个元素,第 3 个(14)就命中了,可循环还得老老实实把剩下 3 个走完。而且为了不被后面的 21、35 覆盖掉结果,你被迫加了 `found == -1` 这个别扭的条件。
有了 break,逻辑立刻回归本意:
```java
int found = -1;
for (int num : nums) {
if (num % 7 == 0) {
found = num;
break; // 找到就走人,不多跑一步
}
}
条件干净了,跑的圈数也少了。数组要是有一百万个元素,这个差别就不只是好看的问题。
break:立刻结束整个循环
break 一旦执行,循环直接终止,不管条件还成不成立。
public class BreakDemo {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
if (i == 5) {
break; // 碰到 5 就停下
}
System.out.println(i);
}
}
}
```java
上面只打印 1 到 4,第 5 轮一进循环体就 break,整个 for 结束。常用来「找到了就走人」,比如在一堆数里找第一个偶数。
break 之后的代码永远执行不到,编译器会直接拦下来:
```java
for (int i = 0; i < 5; i++) {
break;
System.out.println(i); // 永远到不了这
}
BreakDemo.java:5: error: unreachable statement
System.out.println(i);
^
```bash
`unreachable statement` 意思是「这条语句没有任何路径能走到」。Java 把它当编译错误而不是警告,逼你把死代码删掉。
### break 只跳一层
这是个高频误解:break 结束的是**离它最近的那一层循环**,不是全部循环。
```java
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) {
break; // 只结束内层,外层照跑
}
System.out.println(i + "," + j);
}
}
输出是 1,1、2,1、3,1 三行。内层每次被 break 掐断,但外层的 i 继续往下走。想一次跳出两层,得用后面讲的标签。
continue:跳过本轮,进下一轮
continue 不结束循环,只跳过当前这一轮剩下的代码,直接开始下一轮。
public class ContinueDemo {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // 跳过 3,不打印
}
System.out.println(i);
}
}
}
```bash
打印 1、2、4、5,第 3 轮被跳过了。适合「某些情况这轮不处理,但后面还要继续」的场景。
> [!NOTE]
> break 是「整个停」,continue 是「这轮停、后面还跑」。别记混:一个管全局退出,一个管本轮跳过。
### continue 在 while 里的经典陷阱
在 for 里用 continue 很安全,因为步进段 `i++` 是循环结构的一部分,continue 之后照样会执行。但在 while 里,步进语句通常写在循环体最后一行,continue 会把它一起跳过:
```java
int i = 0;
while (i < 5) {
if (i == 2) {
continue; // 跳过后面所有代码,包括下面的 i++
}
System.out.println(i);
i++; // i == 2 时永远执行不到
}
i 卡在 2 上,条件 2 < 5 永远成立,程序死在这里。控制台什么都不再输出,CPU 一个核被吃满。
改法是把步进提到 continue 之前:
int i = 0;
while (i < 5) {
int cur = i;
i++; // 先前进,保证每轮都会动
if (cur == 2) {
continue;
}
System.out.println(cur);
}
```java
更省事的做法是换成 for,让步进段脱离循环体:
```java
for (int k = 0; k < 5; k++) {
if (k == 2) {
continue; // for 的 k++ 照常执行,不会卡住
}
System.out.println(k);
}
Warning循环体里出现 continue 时,先确认「改变条件的那句」在不在 continue 后面。在的话就是个定时炸弹。这条规则记住能省掉半夜排查死循环的功夫。
两者速查对照
| 对比项 | break | continue |
|---|---|---|
| 作用 | 终止整个循环 | 结束本轮,进入下一轮 |
| 影响范围 | 最近一层循环(或 switch) | 最近一层循环 |
| 后面的循环还跑吗 | 不跑了 | 继续跑 |
| 能用在 switch 里吗 | 能,用来防穿透 | 不能,只能用在循环里 |
| 能带标签吗 | 能 | 能 |
| 典型场景 | 找到目标就退出、条件触发就中止 | 过滤掉不需要处理的元素 |
用错地方编译器会拦:
Demo.java:6: error: continue outside of loop
continue;
^
```text
```text
Demo.java:9: error: break outside switch or loop
break;
^
看到这两条,检查是不是把语句写到了循环或 switch 的花括号外面,或者写进了 lambda 表达式里面(lambda 内部不能 break 外层循环)。
switch 里的 break 不是一回事
break 在 switch 里也能用,但语义不同:它是用来防止「穿透」到下一个 case,跟循环没关系。
// 老写法:每个 case 都要手动补 break,漏一个就穿透
int day = 2;
switch (day) {
case 1:
System.out.println("周一");
break;
case 2:
System.out.println("周二");
break; // 漏掉这行,会继续打印「其他」
default:
System.out.println("其他");
}
```bash
漏写 break 是老 switch 的经典事故。Java 14 起正式支持箭头写法,从语法上根除了这个坑:
```java
// Java 14+ 推荐:箭头写法不穿透,不需要 break
int day = 2;
switch (day) {
case 1 -> System.out.println("周一");
case 2 -> System.out.println("周二");
default -> System.out.println("其他");
}
新代码优先用箭头写法。传统的冒号写法依然合法(读老项目会遇到),但不建议在新代码里用。switch 的完整用法在第 20 章已经展开过。
Note循环里套了 switch 时要格外小心:switch 里的 break 只会跳出 switch,不会跳出外面的循环。想在 switch 里直接结束循环,得给循环贴标签,然后写
break 标签名。
标签:在嵌套循环里精准跳出
嵌套循环时,普通的 break 只能跳出最内层。想直接从外层跳出,得给外层循环贴个标签。
public class LabelDemo {
public static void main(String[] args) {
outer: // 标签名加冒号
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (i == 2 && j == 2) {
break outer; // 跳到 outer 标签之外
}
System.out.println(i + "," + j);
}
}
}
}
```text
`break outer;` 直接结束贴了 `outer:` 标签的那层循环,而不是只结束内层。标签名随便起,但习惯上用小写。
输出是这四行:
```text
1,1
1,2
1,3
2,1
走到 i=2、j=2 时命中条件,两层循环同时终止,后面的 2,3、3,1 全都不会出现。对比一下:如果只写普通的 break,3,1 这些还会照常打印。
标签名写错,编译器直接报错:
LabelDemo.java:8: error: undefined label: outter
break outter;
^
```bash
`undefined label` 说明这个名字没定义过。检查标签声明处的拼写,以及标签是不是贴在了要跳出的那层循环上——标签必须写在循环语句的正前方,中间不能隔别的语句。
## continue 也能带标签
continue 同样能带标签,作用是跳到标签所在循环的下一次迭代:
```java
public class ContinueLabelDemo {
public static void main(String[] args) {
outer:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) {
continue outer; // 直接开始外层的下一轮
}
System.out.println(i + "," + j);
}
}
}
}
输出只有 1,1、2,1、3,1。每次 j 走到 2,就放弃内层剩下的迭代,回到外层让 i 加 1 后重新开始。
把它和不带标签的 continue 对比:不带标签时,continue 只跳过内层的这一轮,内层还会继续跑 j=3;带上 outer 后,整个内层循环被放弃。
该不该用标签
标签能解决问题,但也是把双刃剑。
Warning标签不是越多越好。滥用标签跳转会让代码像「goto」一样难读。多数情况靠方法抽取或加标志变量就能避开,标签只在没有更干净写法时才用。
两种常见的替代方案:
方案一:把嵌套循环抽成方法,用 return 退出。
public static int[] findPair(int[][] matrix, int target) {
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == target) {
return new int[]{i, j}; // return 天然跳出所有层
}
}
}
return null; // 没找到
}
```java
`return` 比 `break outer` 更直白:这个方法的任务完成了,走人。顺带还把「查找」这件事变成了一个有名字、能复用、能单独测试的方法。
**方案二:用标志变量控制外层条件。**
```java
boolean found = false;
for (int i = 0; i < 3 && !found; i++) {
for (int j = 0; j < 3; j++) {
if (i == 2 && j == 2) {
found = true;
break; // 只跳内层
}
}
}
外层条件加上 && !found,内层置位后外层自然停。缺点是多了个变量、条件变长,两层还行,三层就绕了。
一条实用的取舍:两层嵌套优先抽方法用 return,三层以上且抽不出方法时才考虑标签。 真用了标签,标签名要起得有意义,outer、searchLoop 都比 a、l1 强。
Tip面试常问 break 和 continue 区别。一句话:break 终止循环,continue 跳过当轮。带标签的版本只是把作用范围从「最近一层」扩大到「指定那层」。
常见疑问
问:标签只能贴在循环上吗?
不是。Java 允许给任意语句块贴标签,然后用 break 标签名 从块中跳出:
block: {
if (x < 0) {
break block; // 直接跳到块结束
}
System.out.println("x 非负");
}
```java
不过这种用法极少见,可读性也一般。`continue` 就不同了,它只能配循环用,贴在普通块上的标签不能被 continue 引用。
**问:`break` 能跳出 if 吗?**
不能。if 不是循环,`break` 在 if 里指的是包着这个 if 的循环或 switch。如果 if 外面既没循环也没 switch,就会报 `break outside switch or loop`。想提前结束一段 if 逻辑,用 return 或者把条件反过来写。
**问:一个循环里能写多个 break 吗?**
能,语法上没限制。但两三个 break 散在不同分支里,读代码的人得把整个循环体扫一遍才敢确定退出时机。这时候通常意味着这段逻辑该拆成方法了——拆完每个出口变成一个 return,意图反而更清楚。