# if / else：让程序学会“看情况办事”

if / else / else if 的写法与判断逻辑：布尔条件从哪来、花括号如何划定分支、命中即停的多路规则，以及 == 与 =、悬挂 else 等新手坑

> 条件分支 · 控制流 · 比较与逻辑运算符 · 约 7 分钟 · 09 月 07 日

## 本篇要点

1. if 的骨架是“if (条件) { 语句 }”，括号里必须是一个 boolean；比较运算符（>、>=、<、<=、==、!=）当场算出一个 true 或 false，这正是 boolean 最常见的来源。
2. if 可以不带 else（假就跳过），也可以配 else（二选一、互斥）；判断相等必须写两个等号 ==，单个 = 是“装入值”，用在 if 里要么编译报错、要么造成恒真的静默 bug。
3. else if 不是新关键字，是“else 后面再跟一个 if”；整条链自上而下逐条检查、第一个为真的分支执行完就整链结束，所以条件的顺序本身就是逻辑的一部分。
4. 花括号把多条语句圈成一个语句块，划定分支的地盘；省略花括号时分支只管辖下一条语句，并且 else 会就近配对它前面最近的 if——缩进不算数，悬挂 else 因此成为经典 bug。
5. 每次写 if 都带花括号，哪怕分支只有一行，能同时躲开“只管一条语句”和“else 配错层”两类问题。

---

前两篇我们建立了一个印象：程序从 `main` 的第一行开始，逐句往下执行到结尾，像一条流水线。上一篇里代码打印 `age`、打印 `height`——变量的值可以换，但程序干的活永远一样：它没法回答“成绩到 60 就打印‘及格’，不到就打印‘不及格’”这种需求。

真实程序恰恰靠这个能力活着：余额不足就拒绝支付，闹钟到点才响铃。Java 把这种能力叫做**条件分支**，最基础的写法是 `if / else`。本篇只解决一个问题：程序如何根据一个“真还是假”的条件，选择执行哪一段语句。循环（让一段代码反复执行）留到下一篇。

先看一个完整例子：

```java
int score = 85;

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

运行结果只打印“及格”。读法是：如果 `score >= 60` 这个判断为真，就执行第一对花括号里的语句；否则执行 `else` 后那一对花括号里的语句。

## if 的骨架：括号里必须是一个 boolean

`if` 的句式固定为 `if (条件) { 语句 }`，括号里的条件必须是一个 boolean。上一篇我们认识了 boolean 类型——变量只能装 `true` 或 `false`。现在它派上了真正的用场：`if` 靠这个值决定走哪条路。

布尔值最常见的来源不是变量，而是**比较运算符**。比较就像一次提问，答案只有“是”或“否”：

| 运算符 | 含义 | 为真的例子 |
|---|---|---|
| `>` | 大于 | `5 > 3` |
| `>=` | 大于等于 | `5 >= 5` |
| `<` | 小于 | `3 < 5` |
| `<=` | 小于等于 | `3 <= 3` |
| `==` | 相等（注意是两个等号） | `score == 90` |
| `!=` | 不相等 | `score != 90` |

所以 `score >= 60` 本身就是个 boolean 值——它不是在描述一个已经存在的事实，而是当场算出一个 true 或 false。

更复杂的条件可以组合出来：`&&` 读作“且”（两边都为真才为真），`||` 读作“或”（只要一边为真就为真），`!` 读作“非”（把 true 变 false、false 变 true）。官方教程特别指出，`&&` 和 `||` 有“短路”行为：右边只在必要时才会被求值 [2]。例如：

```java
if (score >= 90 && score <= 100) {   // 只有 90 到 100 之间才为真
    System.out.println("成绩在优秀区间");
}
```

这里能再次看到强类型的把关：`if` 括号里只接受 boolean。C 语言允许 `if (1)`（把 1 当“真”用），Java 里这样写直接编译报错——javac 在运行前就用上一篇说的那张“类型规格单”拦下了你。

`if` 也可以不带 `else` 单独使用：条件为假时什么都不做，程序直接跳到 `if` 语句之后继续往下走。

## else：为“条件不成立”准备第二条路

`if` 后面跟 `else`，读作“否则”。走 `if` 分支还是 `else` 分支，取决于括号里算出的值，两条路互相排斥，同一时刻只会执行其中一支。上一节那个打印“及格/不及格”的例子，就是最标准的二选一。

## 多条路：else if 自上而下、命中即停

真实判断常常不止两个分支。比如分数分四档：

```java
int score = 85;

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

输出“良好”。这条链有两个关键规则。第一，**自上而下**逐条检查；第二，**命中即停**——`85 >= 80` 为真，打印完“良好”后整条链立即结束，后面的条件不再看，哪怕 `85 >= 60` 也为真。Java 官方教程用同一个等第例子专门点明：一旦某个条件被满足，就执行相应语句，剩余条件不再被求值 [1]。

```mermaid
flowchart TD
    A[开始] --> B{score >= 90}
    B -- 真 --> C[打印优秀，链结束]
    B -- 假 --> D{score >= 80}
    D -- 真 --> E[打印良好，链结束]
    D -- 假 --> F{score >= 60}
    F -- 真 --> G[打印及格，链结束]
    F -- 假 --> H[打印不及格]
```

“命中即停”有个直接后果：**条件的顺序本身就是程序逻辑的一部分**。如果把 `score >= 60` 挪到最前面，那么 90 分也会先命中它、打印“及格”后停住，“优秀”永远轮不到。写 `else if` 链时，越苛刻的条件要放越前面。

还有一点：`else if` 并不是什么新语法，它就是“`else` 后面再跟一个 `if`”，读作“否则，再看下一个条件”。拆开看就明白为什么链条能无限接下去。

## 花括号：决定哪些语句属于这个分支

一个分支里当然可以放多条语句。`if` 后面跟的**语句块**（block）就是一对花括号把若干语句圈成一个整体 [3]。花括号划定了每个分支的地盘。

如果偷懒不写花括号，分支只管辖紧随其后的**第一条**语句：

```java
if (score >= 60)
    System.out.println("及格");
    System.out.println("欢迎再来");    // 无条件执行！
```

缩进让“欢迎再来”看起来像分支的一部分，但 Java 不看缩进——它只看结构。没有花括号，`if` 只绑定了第一条打印语句，第二行不管分数多少都会执行。Java 官方教程对 block 的定义值得记住：一对花括号之间的语句组成了一个整体，凡是允许写一条语句的地方都可以放一个 block [3]。

省略花括号还埋伏着更隐蔽的事故，叫**悬挂 else**（dangling else）。配对规则是：`else` 总是配对它前面最近的、还没配对的 `if`，缩进不参与配对。看这段代码：

```java
int score = 92;

if (score >= 90)
    if (score >= 95)
        System.out.println("A+");
else
    System.out.println("低于 90");
```

缩进暗示 `else` 属于外层 `if (score >= 90)`——意图是分数不到 90 时提示“低于 90”。但按 Java 规则，`else` 配的是内层 `if (score >= 95)`：92 分时会打印出荒谬的“低于 90”。改成 80 分，外层条件为假，整段直接跳过，本应出现的提示也没了。

两个事故的修法完全相同——每层都写花括号：

```java
if (score >= 90) {
    if (score >= 95) {
        System.out.println("A+");
    } else {
        System.out.println("90 到 94 之间");
    }
} else {
    System.out.println("低于 90");
}
```

把“每层 `if` 都带花括号”练成习惯，哪怕分支只有一行。这不是风格偏好，是规避一类真实 bug 的最低成本手段。

## 判断相等写 ==，赋值才写 =

最后回到那个最容易踩的坑：`==` 和 `=`。上一篇我们学过，`=` 是“装入”——先算右边，再把结果写进左边变量；而 `if` 需要的“问一问两边是否相等”是 `==` 的职责。弄混了会怎样？

```java
int score = 85;
if (score = 90) {    // 编译报错：int 不是 boolean
    ...
}
```

这行 Java 编译不过：javac 检查类型时发现括号里是个 int，不是 boolean。强类型在这里救了你一次——换成 C 语言，这行会顺利通过，还会顺便把 score 改成 90（连你的数据都污染了）。

但有个更阴险的变体。如果左边恰好是 boolean 变量：

```java
boolean isStudent = false;
if (isStudent = true) {    // 能编译！但这不是判断，是赋值
    ...
}
```

`if (isStudent = true)` 能通过编译：它做的不是“问 isStudent 是否为 true”，而是“把 true 装进 isStudent”，于是条件恒为真，这个分支永远被走进。你盯着代码看，以为自己在做判断，其实在做赋值——这是最难排查的一类静默 bug。记忆法：`=` 改变变量的值，`==` 不改变任何东西、只产出一个 true 或 false。判断用 `==`，赋值用 `=`。

到这里，条件分支的核心闭环已经完成：`if` 是“括号里的 boolean 为真才执行花括号内的语句”；`else` 兜底、`else if` 提供自上而下、命中即停的多路判断；花括号划定分支地盘，`==` 负责提问，`=` 负责装值。程序从此会“看情况办事”了，但还有一种更常见的需求它还不满足——让同一段代码反复执行，直到某个条件改变。下一篇就轮到 while 和 for。

## 来源

1. [Java 官方教程：if-then 与 if-then-else 语句，含等第示例及“条件满足后剩余条件不再求值”的说明](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/if.html)
2. [Java 官方教程：相等、关系与条件运算符，含 &&、|| 的短路行为说明](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op2.html)
3. [Java 官方教程：表达式、语句与语句块（block 的定义）](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html)

---

原文：https://pangzhengboyin.com/articles/java-if-else-conditional-branching-6f704f49

> **庞征博引** · 想学的，慢慢都会
>
> 庞征博引是把想学的东西写成连载的 AI 学习工具。说出想学什么，它会先了解你的基础，再把主题写成一篇篇 5–10 分钟能读完的文章；边读边问，接下来学什么跟着你走。这篇就是这样写出来的。
>
> 开始你自己的连载 → https://pangzhengboyin.com
