第三篇:Java 流程控制详解:条件判断、循环与跳转语句

前言

如果把程序比作一条路,那么流程控制语句就是路上的红绿灯、岔路口和指示牌。

默认情况下,Java 代码会按照从上到下的顺序依次执行。但真实业务显然不会永远"一条路走到底":

  • 用户登录成功和失败,需要执行不同的逻辑;
  • 批量处理订单,需要重复执行同一段代码;
  • 查找到目标数据后,希望提前结束循环;
  • 遇到无效数据时,希望跳过本轮处理;
  • 某些条件不满足时,希望直接结束整个方法。

这些需求分别对应 Java 中的条件判断、循环结构和跳转语句。

本文将系统梳理以下内容:

  • ifif-else 和多分支判断
  • 传统 switch 与增强 switch
  • for、增强 forwhiledo-while
  • breakcontinuereturn
  • 标签语句与多层循环跳转
  • 常见错误及实际开发建议
  • 一个综合案例

一、先理解什么是流程控制

Java 中常见的程序执行结构可以分为三类。

1. 顺序结构

顺序结构是最简单的结构,代码从上到下依次执行。

复制代码
public class Demo {
    public static void main(String[] args) {
        System.out.println("第一步:打开冰箱");
        System.out.println("第二步:放入饮料");
        System.out.println("第三步:关闭冰箱");
    }
}

执行结果:

复制代码
第一步:打开冰箱
第二步:放入饮料
第三步:关闭冰箱

这里没有判断,也没有循环,执行顺序和代码书写顺序完全一致。

2. 分支结构

分支结构会根据条件选择不同的执行路线,常用语句包括:

  • if
  • if-else
  • if-else if-else
  • switch

3. 循环结构

循环结构用于重复执行某段代码,常用语句包括:

  • for
  • 增强 for
  • while
  • do-while

除此之外,Java 还提供了 breakcontinuereturn 等语句,用来改变程序原来的执行方向。


二、条件判断:if 语句

2.1 单分支 if

当某段代码只需要在条件成立时执行,可以使用单分支 if

语法如下:

复制代码
if (条件表达式) {
    条件成立时执行的代码;
}

例如,判断用户是否成年:

复制代码
int age = 20;

if (age >= 18) {
    System.out.println("用户已经成年");
}

age >= 18 的结果为 true 时,才会执行大括号中的代码。

需要特别注意:Java 的条件表达式必须是 boolean 类型,不能像某些语言一样使用整数代替真假。

下面的写法无法通过编译:

复制代码
int number = 1;

// 错误:int 不能自动转换为 boolean
if (number) {
    System.out.println("条件成立");
}

正确写法应该明确给出判断条件:

复制代码
if (number != 0) {
    System.out.println("number 不等于 0");
}

2.2 双分支 if-else

当程序需要在两种结果中选择一种时,可以使用 if-else

复制代码
int score = 58;

if (score >= 60) {
    System.out.println("考试及格");
} else {
    System.out.println("考试不及格");
}

它的逻辑很直接:

  • 条件为 true,执行 if 中的代码;
  • 条件为 false,执行 else 中的代码。

两个分支只会执行其中一个。


2.3 多分支 if-else if-else

如果业务中存在多个区间,可以使用多分支结构。

复制代码
int score = 86;

if (score >= 90) {
    System.out.println("等级:优秀");
} else if (score >= 80) {
    System.out.println("等级:良好");
} else if (score >= 60) {
    System.out.println("等级:及格");
} else {
    System.out.println("等级:不及格");
}

程序会从上到下依次判断,一旦某个条件成立,后面的分支就不会再执行。

因此,多分支条件的顺序很重要。

下面这种写法就有问题:

复制代码
int score = 95;

if (score >= 60) {
    System.out.println("及格");
} else if (score >= 90) {
    System.out.println("优秀");
}

虽然分数是 95,但第一个条件 score >= 60 已经成立,所以程序只会输出"及格","优秀"分支永远没有机会执行。

对于成绩、价格、年龄这类区间判断,通常应该从范围更严格的条件开始判断:

复制代码
if (score >= 90) {
    System.out.println("优秀");
} else if (score >= 60) {
    System.out.println("及格");
} else {
    System.out.println("不及格");
}

2.4 嵌套 if

if 内部还可以继续使用 if

复制代码
int age = 22;
boolean hasTicket = true;

if (age >= 18) {
    if (hasTicket) {
        System.out.println("允许入场");
    } else {
        System.out.println("请先购票");
    }
} else {
    System.out.println("未成年人不能入场");
}

嵌套判断本身没有问题,但层数太多时,代码会越来越难读。

实际开发中,可以使用逻辑运算符简化:

复制代码
if (age >= 18 && hasTicket) {
    System.out.println("允许入场");
}

也可以通过提前 return 减少嵌套。


2.5 逻辑运算与短路判断

条件判断经常会配合下面几个逻辑运算符:

运算符 含义 示例
&& 并且,两个条件都成立 age >= 18 && hasTicket
|| 或,成立一个即可 age>=18 && !hasTicket
! 取反 !isLogin

&&|| 具有短路特性。

例如:

复制代码
String username = null;

if (username != null && !username.isBlank()) {
    System.out.println("用户名有效");
}

程序先判断 username != null

因为这个条件为 false,整个 && 表达式已经不可能成立,所以右边的 username.isBlank() 不会执行,也就避免了空指针异常。

如果把顺序反过来:

复制代码
if (!username.isBlank() && username != null) {
    System.out.println("用户名有效");
}

程序会先调用 username.isBlank(),此时就会抛出 NullPointerException

因此,"先判空,再调用对象的方法"是一个很实用的习惯。


2.6 三元运算符:简单判断的简写

对于非常简单的二选一赋值,可以使用三元运算符:

复制代码
条件 ? 条件成立时的结果 : 条件不成立时的结果

例如:

复制代码
int age = 20;
String result = age >= 18 ? "成年人" : "未成年人";

System.out.println(result);

三元运算符适合简单场景。如果判断逻辑很长,继续使用 if-else 通常更清楚。

不建议写出层层嵌套的三元表达式:

复制代码
String level = score >= 90 ? "优秀"
        : score >= 80 ? "良好"
        : score >= 60 ? "及格"
        : "不及格";

它虽然能运行,但阅读成本并不低。代码不是越短越好,能让别人快速看懂更加重要。


三、多值判断:switch 语句

当一个变量需要与多个确定值进行匹配时,switch 往往比一长串 if-else 更直观。

3.1 传统 switch 写法

复制代码
int day = 3;

switch (day) {
    case 1:
        System.out.println("星期一");
        break;
    case 2:
        System.out.println("星期二");
        break;
    case 3:
        System.out.println("星期三");
        break;
    case 4:
        System.out.println("星期四");
        break;
    case 5:
        System.out.println("星期五");
        break;
    case 6:
    case 7:
        System.out.println("周末");
        break;
    default:
        System.out.println("输入的日期不合法");
}

传统 switch 中几个关键字的作用如下:

  • case:定义一个可以匹配的值;
  • break:结束当前 switch
  • default:所有 case 都不匹配时执行。

default 不是语法上绝对必需的,但保留它可以处理意料之外的数据。


3.2 case 穿透现象

传统 switch 中,如果某个 case 后面没有写 break,程序会继续执行后面的分支,这种现象叫作 case 穿透。

复制代码
int number = 1;

switch (number) {
    case 1:
        System.out.println("A");
    case 2:
        System.out.println("B");
    case 3:
        System.out.println("C");
    default:
        System.out.println("结束");
}

输出结果:

复制代码
A
B
C
结束

很多初学者会觉得:number 明明等于 1,为什么后面的内容也执行了?

原因是程序从 case 1 进入后,一直没有遇到 break,于是继续向下执行。

当然,穿透并不总是错误。有时可以利用它合并分支:

复制代码
int month = 2;

switch (month) {
    case 1:
    case 2:
    case 3:
        System.out.println("第一季度");
        break;
    case 4:
    case 5:
    case 6:
        System.out.println("第二季度");
        break;
    default:
        System.out.println("其他月份");
}

不过,只要不是故意使用穿透,最好及时写上 break


3.3 增强 switch

从 JDK 14 开始,switch 表达式已经成为正式特性,可以使用箭头语法:

复制代码
int day = 6;

switch (day) {
    case 1 -> System.out.println("星期一");
    case 2 -> System.out.println("星期二");
    case 3 -> System.out.println("星期三");
    case 4 -> System.out.println("星期四");
    case 5 -> System.out.println("星期五");
    case 6, 7 -> System.out.println("周末");
    default -> System.out.println("日期不合法");
}

箭头写法不会发生传统 case 穿透,因此不需要在每个分支后面写 break

增强 switch 还可以直接产生一个值:

复制代码
int day = 6;

String dayType = switch (day) {
    case 1, 2, 3, 4, 5 -> "工作日";
    case 6, 7 -> "休息日";
    default -> "无效日期";
};

System.out.println(dayType);

如果一个分支中需要执行多行代码,可以使用代码块和 yield

复制代码
int level = 2;

String description = switch (level) {
    case 1 -> "普通会员";
    case 2 -> {
        System.out.println("正在加载高级会员权益");
        yield "高级会员";
    }
    case 3 -> "超级会员";
    default -> "未知等级";
};

System.out.println(description);

这里的 yield 表示为整个 switch 表达式提供一个结果,它和结束方法的 return 不是一回事。


3.4 if 和 switch 应该怎么选

我通常按照下面的思路来选择:

更适合使用 if 的情况

  • 判断的是范围,例如分数、年龄、价格;

  • 条件中包含 &&|| 等复杂逻辑;

  • 每个分支判断的变量不完全相同;

  • 只有一两个简单分支。

    if (age >= 18 && hasTicket) {
    System.out.println("允许入场");
    }

更适合使用 switch 的情况

  • 围绕同一个变量进行判断;

  • 判断的是几个确定值;

  • 状态、类型、菜单选项比较多;

  • 希望直接根据选项产生一个结果。

    String text = switch (status) {
    case 0 -> "待支付";
    case 1 -> "已支付";
    case 2 -> "已发货";
    case 3 -> "已完成";
    default -> "未知状态";
    };


四、循环结构

循环解决的是"重复执行"问题。

一个完整的循环通常包含四部分:

  1. 初始化循环变量;
  2. 判断循环条件;
  3. 执行循环体;
  4. 更新循环变量。

如果更新变量或终止条件写错,程序很容易陷入死循环。


4.1 for 循环

for 循环适合循环次数比较明确的场景。

基本语法:

复制代码
for (初始化语句; 循环条件; 更新语句) {
    循环体;
}

例如,输出 1 到 5:

复制代码
for (int i = 1; i <= 5; i++) {
    System.out.println("当前数字:" + i);
}

执行过程可以理解为:

复制代码
初始化 i = 1
      ↓
判断 i <= 5
      ↓
执行循环体
      ↓
执行 i++
      ↓
重新判断 i <= 5

初始化语句只执行一次,条件判断和变量更新会重复执行。


4.2 使用 for 完成累加

计算 1 到 100 的和:

复制代码
int sum = 0;

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

System.out.println("1 到 100 的和为:" + sum);

计算 1 到 100 之间所有偶数的和:

复制代码
int sum = 0;

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

System.out.println("偶数之和:" + sum);

也可以从 1 开始循环,然后在循环内部判断:

复制代码
int sum = 0;

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

前一种写法循环次数更少,后一种写法更容易看出"筛选偶数"的过程。实际开发时,应优先保证逻辑清楚,再考虑这类微小优化。


4.3 for 循环中的变量作用域

如果循环变量定义在 for 内部,那么它只能在循环内部使用。

复制代码
for (int i = 0; i < 3; i++) {
    System.out.println(i);
}

// 这里无法访问 i
// System.out.println(i);

如果循环结束后仍然需要使用变量,可以把它声明在循环外部:

复制代码
int i;

for (i = 0; i < 3; i++) {
    System.out.println(i);
}

System.out.println("循环结束后的 i:" + i);

4.4 增强 for 循环

增强 for 也经常被称为 foreach,适合遍历数组或集合。

复制代码
int[] scores = {90, 76, 88, 59};

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

它表示:每次从 scores 中取出一个元素,放入变量 score

增强 for 写起来简洁,但它也有明显限制:

  • 不方便获取当前下标;
  • 不适合需要倒序遍历的场景;
  • 不方便指定步长;
  • 给循环变量重新赋值,通常不会修改原数组元素。

例如:

复制代码
int[] numbers = {1, 2, 3};

for (int number : numbers) {
    number = number * 10;
}

for (int number : numbers) {
    System.out.println(number);
}

输出仍然是:

复制代码
1
2
3

因为 number 只是当前数组元素值的一份副本。

如果确实需要修改数组元素,可以使用普通 for

复制代码
for (int i = 0; i < numbers.length; i++) {
    numbers[i] = numbers[i] * 10;
}

4.5 while 循环

while 适合"暂时不知道需要循环多少次,只知道什么时候结束"的场景。

语法如下:

复制代码
while (条件表达式) {
    循环体;
}

例如:

复制代码
int count = 1;

while (count <= 5) {
    System.out.println("第 " + count + " 次执行");
    count++;
}

while 会先判断条件,再决定是否执行循环体。如果条件一开始就是 false,循环体一次也不会执行。

复制代码
int number = 10;

while (number < 5) {
    System.out.println(number);
}

上面的代码不会产生任何输出。


4.6 while 的典型使用场景

例如,模拟任务重试:

复制代码
int retryCount = 0;
boolean success = false;

while (!success && retryCount < 3) {
    retryCount++;

    System.out.println("正在进行第 " + retryCount + " 次尝试");

    if (retryCount == 2) {
        success = true;
    }
}

if (success) {
    System.out.println("任务执行成功");
} else {
    System.out.println("任务执行失败");
}

在这个例子中,我们不知道任务会在第几次成功,只知道:

  • 成功后停止;
  • 最多尝试三次。

这就是比较典型的 while 场景。


4.7 do-while 循环

do-whilewhile 的最大区别是:do-while 会先执行一次循环体,再判断条件。

语法如下:

复制代码
do {
    循环体;
} while (条件表达式);

注意最后的分号不能漏掉。

例如:

复制代码
int number = 10;

do {
    System.out.println("number = " + number);
    number++;
} while (number < 5);

虽然 number < 5 一开始就是 false,但程序仍然会输出一次:

复制代码
number = 10

因此,do-while 适合至少需要执行一次的场景,例如:

  • 第一次显示系统菜单;
  • 第一次读取用户输入;
  • 第一次执行连接操作;
  • 执行操作后询问用户是否继续。

4.8 三种循环怎么选

循环类型 特点 常见场景
for 循环次数通常比较明确 计数、按下标遍历数组
增强 for 写法简洁,不关心下标 只读取数组或集合元素
while 先判断,可能一次不执行 重试、等待、条件未知的循环
do-while 先执行,至少执行一次 菜单、输入校验、交互程序

这并不是强制规定。很多场景用不同循环都能实现,选择最容易表达业务含义的写法即可。


五、循环嵌套

循环内部可以继续编写循环,这叫作嵌套循环。

例如,打印乘法表:

复制代码
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();
}

外层循环控制行数,内层循环控制每一行输出多少个表达式。

再看一个遍历二维数组的例子:

复制代码
int[][] matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
};

for (int row = 0; row < matrix.length; row++) {
    for (int col = 0; col < matrix[row].length; col++) {
        System.out.print(matrix[row][col] + " ");
    }

    System.out.println();
}

这段代码的运行结果是:

复制代码
1 2 3 
4 5 6 
7 8 9 

外层循环控制行数,内层循环控制列数,每一行输出结束后调用 System.out.println() 换行。

嵌套循环非常常见,但层数不宜过深。三四层循环叠在一起时,通常已经很难阅读,也可能带来明显的性能问题。


六、跳转语句

6.1 break:立即结束循环

break 用于立即结束当前循环。

复制代码
for (int i = 1; i <= 10; i++) {
    if (i == 5) {
        break;
    }

    System.out.println(i);
}

输出结果:

复制代码
1
2
3
4

i == 5 时执行 break,整个循环立即结束。

需要注意:break 结束的是离它最近的一层循环。

复制代码
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        if (j == 2) {
            break;
        }

        System.out.println("i = " + i + ", j = " + j);
    }
}

这里的 break 只会结束内层循环,外层循环仍然会继续。


6.2 continue:跳过本次循环

continue 不会结束整个循环,它只会跳过当前这一轮剩余的代码,然后进入下一轮。

复制代码
for (int i = 1; i <= 10; i++) {
    if (i % 2 == 0) {
        continue;
    }

    System.out.println(i);
}

输出结果:

复制代码
1
3
5
7
9

遇到偶数时执行 continue,本轮后面的输出语句会被跳过。

可以简单记成:

  • break:后面的轮次也不执行了;
  • continue:只是不执行本轮剩余代码。

6.3 while 中使用 continue 要小心

下面这段代码会进入死循环:

复制代码
int i = 0;

while (i < 5) {
    if (i == 2) {
        continue;
    }

    System.out.println(i);
    i++;
}

i 等于 2 时,程序直接执行 continue,导致后面的 i++ 被跳过。

于是 i 永远等于 2,循环条件永远成立。

可以改成:

复制代码
int i = 0;

while (i < 5) {
    i++;

    if (i == 2) {
        continue;
    }

    System.out.println(i);
}

或者在执行 continue 之前手动更新变量:

复制代码
int i = 0;

while (i < 5) {
    if (i == 2) {
        i++;
        continue;
    }

    System.out.println(i);
    i++;
}

相比之下,普通 for 执行 continue 后,还会执行更新表达式,所以这种问题在 for 中相对少见。


6.4 return:结束整个方法

return 的作用范围比 break 更大,它结束的是当前整个方法。

复制代码
public static void checkAge(int age) {
    if (age < 0) {
        System.out.println("年龄不能小于 0");
        return;
    }

    if (age < 18) {
        System.out.println("未成年人");
        return;
    }

    System.out.println("成年人");
}

当执行到 return 时,方法后面的代码不再执行。

return 经常用于提前处理异常情况,这种写法也叫"提前返回"或"守卫式写法"。

例如,下面的嵌套比较深:

复制代码
public static void submitOrder(boolean login, boolean hasAddress) {
    if (login) {
        if (hasAddress) {
            System.out.println("订单提交成功");
        } else {
            System.out.println("请先填写地址");
        }
    } else {
        System.out.println("请先登录");
    }
}

使用提前返回后会更加平整:

复制代码
public static void submitOrder(boolean login, boolean hasAddress) {
    if (!login) {
        System.out.println("请先登录");
        return;
    }

    if (!hasAddress) {
        System.out.println("请先填写地址");
        return;
    }

    System.out.println("订单提交成功");
}

这种写法在实际项目中很常见。


6.5 break、continue、return 的区别

语句 作用 影响范围
break 结束循环或传统 switch 当前循环或 switch
continue 跳过当前这一轮 当前循环的一次迭代
return 结束当前方法 整个方法

看一个对比:

复制代码
public static void test() {
    for (int i = 1; i <= 5; i++) {
        if (i == 2) {
            continue;
        }

        if (i == 4) {
            break;
        }

        System.out.println(i);
    }

    System.out.println("循环已经结束");
}

输出:

复制代码
1
3
循环已经结束

如果把 break 改成 return

复制代码
if (i == 4) {
    return;
}

那么"循环已经结束"也不会输出,因为整个 test() 方法已经结束。


七、标签语句:跳出多层循环

Java 没有可以任意跳转的 goto 语句,但支持带标签的 breakcontinue

例如,在二维数组中查找目标值:

复制代码
int[][] matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
};

int target = 5;

outer:
for (int row = 0; row < matrix.length; row++) {
    for (int col = 0; col < matrix[row].length; col++) {
        if (matrix[row][col] == target) {
            System.out.println(
                    "找到目标,位置:" + row + ", " + col
            );

            break outer;
        }
    }
}

System.out.println("查找结束");

如果只写普通 break,只能结束内层循环;写成 break outer,可以直接结束被 outer 标记的外层循环。

同样,也可以使用带标签的 continue

复制代码
outer:
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        if (j == 2) {
            continue outer;
        }

        System.out.println("i = " + i + ", j = " + j);
    }
}

不过,标签语句容易让执行流程变得跳跃。除非确实是在处理多层循环,否则不建议频繁使用。

很多时候,把查找逻辑提取成一个方法,再通过 return 返回结果会更清晰:

复制代码
public static boolean contains(int[][] matrix, int target) {
    for (int[] row : matrix) {
        for (int number : row) {
            if (number == target) {
                return true;
            }
        }
    }

    return false;
}

八、一个综合案例:学生成绩统计

下面通过一个完整案例,把条件判断、循环、continuebreakreturn 串联起来。

需求如下:

  1. 遍历学生成绩;

  2. 跳过不合法的成绩;

  3. 统计有效人数、总分、平均分和最高分;

  4. 统计及格人数;

  5. 如果遇到满分,记录第一位满分学生;

  6. 如果没有有效成绩,直接结束方法。

    public class ScoreStatistics {

    复制代码
     public static void main(String[] args) {
         String[] names = {"小明", "小红", "小刚", "小李", "小周"};
         int[] scores = {86, 102, 59, 100, 76};
    
         printStatistics(names, scores);
     }
    
     public static void printStatistics(String[] names, int[] scores) {
         if (names == null || scores == null) {
             System.out.println("学生信息不能为空");
             return;
         }
    
         if (names.length != scores.length) {
             System.out.println("姓名数量和成绩数量不一致");
             return;
         }
    
         int validCount = 0;
         int passCount = 0;
         int totalScore = 0;
         int highestScore = -1;
    
         for (int i = 0; i < scores.length; i++) {
             int score = scores[i];
    
             if (score < 0 || score > 100) {
                 System.out.println(
                         "跳过无效成绩:" + names[i] + "," + score
                 );
                 continue;
             }
    
             validCount++;
             totalScore += score;
    
             if (score >= 60) {
                 passCount++;
             }
    
             if (score > highestScore) {
                 highestScore = score;
             }
         }
    
         if (validCount == 0) {
             System.out.println("没有可以统计的有效成绩");
             return;
         }
    
         String firstFullScoreStudent = null;
    
         for (int i = 0; i < scores.length; i++) {
             if (scores[i] == 100) {
                 firstFullScoreStudent = names[i];
                 break;
             }
         }
    
         double average = totalScore * 1.0 / validCount;
    
         String level = switch ((int) average / 10) {
             case 10, 9 -> "整体表现优秀";
             case 8 -> "整体表现良好";
             case 7 -> "整体表现中等";
             case 6 -> "整体达到及格水平";
             default -> "整体成绩需要提升";
         };
    
         System.out.println();
         System.out.println("有效人数:" + validCount);
         System.out.println("及格人数:" + passCount);
         System.out.println("最高分:" + highestScore);
         System.out.println("平均分:" + average);
         System.out.println("班级评价:" + level);
    
         if (firstFullScoreStudent != null) {
             System.out.println("第一位满分学生:" + firstFullScoreStudent);
         } else {
             System.out.println("本次没有满分学生");
         }
     }

    }

这个案例中:

  • if 用于参数校验和成绩判断;
  • for 用于遍历数组;
  • continue 用于跳过无效成绩;
  • break 用于找到第一位满分学生后提前结束查找;
  • return 用于参数错误或没有有效成绩时结束方法;
  • 增强 switch 用于生成班级评价。

流程控制语句往往不是各自孤立使用的,真正写业务代码时,它们通常会组合在一起。


九、流程控制中最常见的坑

9.1 if 后面误写分号

复制代码
int age = 10;

if (age >= 18); {
    System.out.println("成年人");
}

这里的分号代表一条空语句,后面的大括号已经不属于 if,所以无论条件是否成立都会执行。

正确写法:

复制代码
if (age >= 18) {
    System.out.println("成年人");
}

9.2 省略大括号带来的问题

Java 允许单条语句省略大括号:

复制代码
if (age >= 18)
    System.out.println("成年人");

但如果后面又增加一行代码:

复制代码
if (age >= 18)
    System.out.println("成年人");
    System.out.println("允许注册");

第二个输出语句并不属于 if

为了避免维护时出现误解,建议即使只有一条语句,也保留大括号:

复制代码
if (age >= 18) {
    System.out.println("成年人");
    System.out.println("允许注册");
}

9.3 使用 == 比较字符串

下面的代码可能得不到预期结果:

复制代码
String role = new String("admin");

if (role == "admin") {
    System.out.println("管理员");
}

== 比较的是两个引用是否指向同一个对象,而不是字符串内容是否相同。

应该使用 equals()

复制代码
if ("admin".equals(role)) {
    System.out.println("管理员");
}

把确定不为 null 的字符串写在前面,还可以避免空指针异常。


9.4 数组边界写错

数组下标从 0 开始,最后一个下标是 length - 1

错误写法:

复制代码
int[] numbers = {10, 20, 30};

for (int i = 0; i <= numbers.length; i++) {
    System.out.println(numbers[i]);
}

i == numbers.length 时,会抛出数组下标越界异常。

正确写法:

复制代码
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}

涉及数组下标时,通常使用 < length,而不是 <= length


9.5 忘记更新循环变量

复制代码
int i = 1;

while (i <= 5) {
    System.out.println(i);
}

这里没有修改 i,所以 i <= 5 永远成立,最终形成死循环。

正确写法:

复制代码
int i = 1;

while (i <= 5) {
    System.out.println(i);
    i++;
}

编写循环时,可以快速检查三个问题:

  1. 循环条件什么时候会变成 false
  2. 循环体是否真的在改变相关变量?
  3. continue 会不会跳过变量更新?

9.6 误以为 switch 中的 break 能结束外层循环

复制代码
while (true) {
    int command = 0;

    switch (command) {
        case 0:
            System.out.println("退出");
            break;
        default:
            System.out.println("继续运行");
    }
}

这里的 break 只会结束 switch,不会结束外面的 while,因此循环仍然会继续。

可以使用布尔变量:

复制代码
boolean running = true;

while (running) {
    int command = 0;

    switch (command) {
        case 0:
            System.out.println("退出");
            running = false;
            break;
        default:
            System.out.println("继续运行");
    }
}

也可以在单独的方法中使用 return,或者在确有必要时使用带标签的 break


9.7 条件嵌套过深

下面这种代码虽然可以运行,但读起来很累:

复制代码
if (user != null) {
    if (user.isLogin()) {
        if (user.hasPermission()) {
            if (order != null) {
                System.out.println("开始处理订单");
            }
        }
    }
}

可以改成提前返回:

复制代码
if (user == null) {
    return;
}

if (!user.isLogin()) {
    return;
}

if (!user.hasPermission()) {
    return;
}

if (order == null) {
    return;
}

System.out.println("开始处理订单");

流程控制写得好不好,关键不在于用了多少语法,而在于执行路径是否清楚。


十、写好流程控制代码的几个建议

1. 条件名称尽量表达业务含义

与其把复杂条件全部堆在 if 中:

复制代码
if (age >= 18 && balance >= 100 && !disabled) {
    // ...
}

不如先提取一个有含义的变量:

复制代码
boolean canPurchase =
        age >= 18
        && balance >= 100
        && !disabled;

if (canPurchase) {
    System.out.println("允许购买");
}

如果判断逻辑还要复用,可以进一步提取成方法。

2. 优先处理异常情况

先处理空值、非法参数和无权限等情况,然后再编写真正的业务逻辑,可以减少嵌套层数。

3. 不要为了少写几行代码牺牲可读性

复杂三元表达式、连续多层嵌套、过度使用标签,都可能让代码看起来很"高级",但维护起来并不轻松。

4. 明确跳转语句的作用范围

写下 breakcontinuereturn 时,最好清楚回答:

  • 它结束的是本轮循环,还是整个循环?
  • 它结束的是内层循环,还是外层循环?
  • 它会不会直接结束整个方法?
  • 它会不会跳过变量更新?

5. 循环首先保证正确,再考虑性能

遍历几百个元素时,清晰通常比微小的性能差异更重要。但如果是多层循环处理大量数据,就需要关注时间复杂度。

例如两个长度都是 n 的嵌套循环,执行次数可能接近 。数据量变大后,性能差距会十分明显。

相关推荐
毕业设计7031 小时前
(免费领源码) SpringBoot 游戏交易平台17600-java、PHP、python、C#、小程序、大数据、单片机、网络工程等)
java·spring boot·mysql·决策树·mybatis·idea·推荐算法
一 乐1 小时前
自习室座位预约管理系统|基于springboot + vue自习室座位预约管理系统(源码+数据库+文档)
java·vue.js·spring boot
m0_734571761 小时前
深入理解5g <二十> dmrs
开发语言·5g
此时不提桶,更待何时2 小时前
02-02-B-AQS与JUC锁面试与生产事故实战
java
音符犹如代码2 小时前
Kafka 接入 AI 的三条路线:MCP 提案、会话记忆与实时上下文
java·大数据·ai·kafka
vx_BS813302 小时前
【项目编号:project51629】Spring Boot 乡村自来水缴费系统:把抄表、水费生成与在线缴费做成一条数字化闭环
java·spring boot·eclipse·tomcat·maven
金金计较.2 小时前
Go语言-4
开发语言·golang
qq_269506752 小时前
第29课-Servlet基础
java
毅炼2 小时前
不写多语言 SDK,怎么让 Python、Go、Node 服务接入注册中心?
java·后端·系统架构·gateway