乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Java概述与环境搭建

    • Java概述与环境搭建
    • Java语言概述
    • 解释型语言与编译型语言对比
    • JDK安装与配置
    • JDK、JRE、JVM 详解
    • HelloWorld程序详解
    • IDE 介绍
  • 第2章 标识符与基本数据类型

    • 章节导读
    • 变量概述
    • 常量概述
    • 基本类型与包装类
    • 字节型 byte
    • 短整型 short
    • 整型 int
    • 长整型 long
    • 单精度浮点型 float
    • 双精度浮点型 double
    • 字符型 char
    • 布尔型 boolean
    • 类型转换
  • 第3章 运算符与表达式

    • 章节导读
    • 算术运算符
    • 赋值运算符
    • 关系运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 运算符优先级
    • 表达式
  • 第4章 流程控制

    • 章节导读
    • 常见的程序运行流程
    • if-else 选择结构
    • switch 多分支选择
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
  • 第5章 数组

    • 章节导读
    • 一维数组
    • 多维数组
    • Arrays 工具类
  • 第6章 类与对象

    • 章节导读
    • 类与对象
    • 方法定义与调用
    • 构造方法
    • 封装
    • 访问修饰符
    • package 与 import
    • static 关键字
    • this 关键字
    • 参数传递 详解
    • 枚举
    • 成员内部类
    • 局部内部类
    • 静态内部类
    • 匿名内部类
  • 第7章 接口与继承

    • 章节导读
    • 继承
    • super 关键字
    • final 关键字
    • 多态
    • 向上转型与向下转型
    • 抽象类
    • 接口
    • 抽象类与接口对比
  • 第8章 注解

    • 章节导读
    • 注解基础
    • 元注解详解
    • 自定义注解
  • 第9章 常用类

    • 章节导读:Java 常用类
    • Object 类:万类之祖
    • 包装类:基本类型的对象化
    • String:不可变的字符串
    • StringBuffer:线程安全的可变字符串
    • StringBuilder:可变的字符串构建器
    • Math:数学运算工具类
    • Random:伪随机数生成器
    • 大数值运算 详解
    • 日期时间API 详解
  • 第10章 异常机制

    • 章节导读
    • 异常体系与分类
    • try-catch-finally
    • try-with-resources
    • throws 与 throw
    • 自定义异常
  • 第11章 泛型

    • 章节导读
    • 泛型基础
    • 通配符与PECS原则
    • 类型擦除
  • 第12章 集合框架

    • 章节导读
    • 集合框架概述
    • ArrayList
    • LinkedList
    • HashMap 详解
    • LinkedHashMap 详解
    • TreeMap 详解
    • HashSet
    • TreeSet 详解
    • TreeSet 与 Comparable
    • Collections 工具类详解
  • 第13章 IO流

    • 章节导读
    • IO流概述
    • 字节流
    • 字符流
    • 缓冲流
    • 转换流 详解
    • 序列化 详解
    • NIO与Files 详解
    • NIO与Files工具类
  • 第14章 多线程与并发

    • 第十六章 多线程与并发 —— 章节导读
    • 线程基础详解
    • synchronized 详解
    • Lock 与显式锁详解
    • volatile 详解
    • wait 与 notify 详解
    • ThreadLocal详解
    • 原子类详解
    • 并发工具类详解
    • 线程池详解
  • 第15章 反射

    • 章节导读
    • 反射概述与 Class 对象
    • Constructor 与对象创建
    • Field与Method详解
    • 反射应用详解
  • 第16章 JDK8新特性

    • 章节导读
    • Lambda 表达式
    • Stream API 基础
    • Stream API 高级详解
    • Optional 详解
    • 新日期时间API详解
  • 第17章 JDK9-11新特性

    • 章节导读
    • 模块化系统 — Project Jigsaw(JDK 9)
    • var 局部变量类型推断(JDK 10)
    • 集合工厂方法与增强(JDK 9 / 10 / 11)
    • 接口增强:private 方法(JDK 9)
    • Stream API 增强(JDK 9)
    • Optional 增强(JDK 9 / 10 / 11)
    • String 新增方法(JDK 11)
    • HTTP Client 与 Files 增强(JDK 11)
    • 直接运行 Java 源文件 — JEP 330(JDK 11)
  • 第18章 JDK12-17新特性

    • 章节导读
    • Switch 表达式(JDK 12 预览 / JDK 14 正式)
    • 文本块 Text Blocks(JDK 13 预览 / JDK 15 正式)
    • Records 记录类(JDK 14 预览 / JDK 16 正式)
    • 密封类 Sealed Classes(JDK 15 预览 / JDK 17 正式)
    • instanceof 模式匹配(JDK 14 预览 / JDK 16 正式)
    • Switch 模式匹配 — Pattern Matching for switch(JDK 17 预览 / JDK 21 正式)
    • Helpful NPE 与 String 增强(JDK 12 / JDK 14 / JDK 15)
    • Stream 增强(JDK 12 / JDK 16)
    • 日期时间增强 — Day Period 支持(JDK 16)
  • 第19章 JDK18-21新特性

    • 章节导读
    • 虚拟线程(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 序列集合(JDK 21 正式)
    • Switch 模式匹配(JDK 17 预览 / JDK 18 第二预览 / JDK 20 第四预览 / JDK 21 正式)
    • Record 模式匹配(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 未命名模式与变量(JDK 21 预览 / JDK 22 正式)
  • 第20章 JDK 22-25 新特性

    • 章节导读
    • 字符串模板(JDK 22 预览 / JDK 23 第二预览 / JDK 24 第三预览)
    • Stream Gatherers(JDK 22 预览 / JDK 24 第二预览)
    • 隐式声明类与实例方法(JDK 23 预览 / JDK 24 第二预览)
    • 原始类型模式匹配(JDK 24 预览)
  • 附录

    • Java 核心知识点
    • Java SE 专业术语
    • Java特性索引(JDK 8 → 25)

if-else 选择结构

"代码是给机器执行的,但首先是给人读的。" —— 大翔在飞翔科技代码评审会上对实习生小崔说,"if-else 看似简单,但嵌套层级、条件顺序、边界处理,每一个细节都体现着程序员的工程素养。"


概述

程序不可能永远按照一条直线执行——它需要像十字路口一样,根据不同条件选择不同的执行路径。if-else 是 Java 中最基础、最灵活的选择结构,它能根据布尔表达式的结果决定代码走向。

三种基本形式

形式语法适用场景
单分支 ifif (条件) { 语句; }只有一个条件满足时执行某操作
双分支 if-elseif (条件) { 语句A; } else { 语句B; }二选一的分支逻辑
多分支 if-else if-elseif (条件1) { ... } else if (条件2) { ... } else { ... }多条件互斥判断

一、单分支 if

语法格式

if (布尔表达式) {
    // 条件为 true 时执行的代码块
}

执行流程:先计算布尔表达式的值,若为 true 则执行花括号内的代码块;若为 false 则跳过。

关键点

关键点说明
条件必须返回 booleanJava 中 if 的条件只能是 boolean 类型,不能像 C 语言那样使用 int
花括号可省略当代码块只有一条语句时可省略 {},但强烈不推荐
条件的副作用条件表达式本身可能有副作用(如方法调用),请确保幂等性

示例:飞翔科技考勤预警

飞翔科技规定:员工当月迟到次数超过 3 次,系统自动发送警告通知。

/**
 * 飞翔科技考勤预警——单分支 if
 */
public class AttendanceWarning {
    public static void main(String[] args) {
        String employeeName = "小崔";
        int lateCount = 4; // 本月迟到次数

        System.out.println("===== 飞翔科技考勤系统 =====");
        System.out.println("员工:" + employeeName);
        System.out.println("本月迟到:" + lateCount + " 次");

        if (lateCount > 3) {
            System.out.println("【警告】" + employeeName
                + ",你本月迟到已超过3次,请于本周五前提交情况说明!");
        }

        System.out.println("考勤检查完毕。");
    }
}
运行输出:
===== 飞翔科技考勤系统 =====
员工:小崔
本月迟到:4 次
【警告】小崔,你本月迟到已超过3次,请于本周五前提交情况说明!
考勤检查完毕。

二、双分支 if-else

语法格式

if (布尔表达式) {
    // 条件为 true 时执行
} else {
    // 条件为 false 时执行
}

执行流程:条件为 true 走 if 分支,为 false 走 else 分支。两条路径必走其一,且只走其一。

示例:绩效达标判断

飞翔科技季度绩效考核中,评分 >= 60 分为"达标",否则为"不达标"。

/**
 * 飞翔科技绩效达标判断——双分支 if-else
 */
public class PerformanceCheck {
    public static void main(String[] args) {
        String name = "朱璐";
        double score = 58.5; // 季度绩效评分

        System.out.println("===== 飞翔科技绩效系统 =====");
        System.out.println("员工:" + name);
        System.out.println("绩效评分:" + score);

        if (score >= 60.0) {
            System.out.println("考核结果:达标 — 继续努力,争取优秀!");
        } else {
            System.out.println("考核结果:未达标 — 请与直属上级制定改进计划。");
        }

        System.out.println("绩效评定完成。");
    }
}
运行输出:
===== 飞翔科技绩效系统 =====
员工:朱璐
绩效评分:58.5
考核结果:未达标 — 请与直属上级制定改进计划。
绩效评定完成。

三、多分支 if-else if-else

语法格式

if (条件1) {
    // 条件1为 true
} else if (条件2) {
    // 条件1为 false 且 条件2为 true
} else if (条件3) {
    // 条件1、2均为 false 且 条件3为 true
} else {
    // 以上条件全部为 false
}

执行流程:从上到下依次判断条件,第一个为 true 的条件对应的代码块将被执行,后续条件不再判断。如果所有条件都不满足,执行 else 分支。

示例:飞翔科技职级薪资定级

飞翔科技技术序列职级(P1-P8),根据职级确定基础薪资范围:

/**
 * 飞翔科技职级薪资定级——多分支 if-else if-else
 */
public class SalaryGrade {
    public static void main(String[] args) {
        String name = "小崔";
        String level = "P5"; // 职级

        System.out.println("===== 飞翔科技薪酬系统 =====");
        System.out.println("员工:" + name + " | 职级:" + level);

        int baseSalary;

        if ("P1".equals(level) || "P2".equals(level)) {
            baseSalary = 8000;
        } else if ("P3".equals(level) || "P4".equals(level)) {
            baseSalary = 15000;
        } else if ("P5".equals(level) || "P6".equals(level)) {
            baseSalary = 25000;
        } else if ("P7".equals(level)) {
            baseSalary = 40000;
        } else if ("P8".equals(level)) {
            baseSalary = 60000;
        } else {
            baseSalary = 0;
            System.out.println("【错误】未知职级:" + level);
            return;
        }

        System.out.println("基础月薪:¥" + baseSalary);
        System.out.println("薪资定级完成。");
    }
}
运行输出:
===== 飞翔科技薪酬系统 =====
员工:小崔 | 职级:P5
基础月薪:¥25000
薪资定级完成。

注意:这里使用 "P5".equals(level) 而非 level.equals("P5"),这是一种防御性编程——当 level 为 null 时前者返回 false,后者抛出 NullPointerException。


四、嵌套 if

语法格式

if (外层条件) {
    if (内层条件1) {
        // 外层true + 内层条件1 true
    } else {
        // 外层true + 内层条件1 false
    }
} else {
    // 外层 false
}

嵌套 if 本质上是将另一个 if-else 结构放在外层 if 的代码块中。每增加一层嵌套,代码复杂度呈指数增长。

示例:学生奖学金评定

飞翔科技与高校联合办学,奖学金评定规则如下:

  • 总成绩 >= 90 且无挂科 → 一等奖学金(¥5000)
  • 总成绩 >= 80 且无挂科 → 二等奖学金(¥3000)
  • 总成绩 >= 70 且无挂科 → 三等奖学金(¥1000)
  • 挂科 → 无奖学金
  • 不满足上述 → 无奖学金
/**
 * 飞翔科技联合办学奖学金评定——嵌套 if
 */
public class Scholarship {
    public static void main(String[] args) {
        String studentName = "小明";
        double avgScore = 91.5; // 平均成绩
        boolean hasFailed = false; // 是否有挂科

        System.out.println("===== 飞翔科技奖学金评定 =====");
        System.out.println("学生:" + studentName);
        System.out.println("平均成绩:" + avgScore);
        System.out.println("挂科情况:" + (hasFailed ? "有挂科" : "无挂科"));

        if (hasFailed) {
            // 外层:有挂科 → 直接淘汰
            System.out.println("评定结果:无奖学金(存在挂科科目)");
        } else {
            // 外层:无挂科 → 进入内层成绩判断
            if (avgScore >= 90) {
                System.out.println("评定结果:一等奖学金 ¥5000");
            } else if (avgScore >= 80) {
                System.out.println("评定结果:二等奖学金 ¥3000");
            } else if (avgScore >= 70) {
                System.out.println("评定结果:三等奖学金 ¥1000");
            } else {
                System.out.println("评定结果:无奖学金(成绩未达标)");
            }
        }

        System.out.println("评定流程结束。");
    }
}
运行输出:
===== 飞翔科技奖学金评定 =====
学生:小明
平均成绩:91.5
挂科情况:无挂科
评定结果:一等奖学金 ¥5000
评定流程结束。

深度原理:if-else 的字节码实现

理解 if-else 在 JVM 层面的实现,可以帮助你写出更高效的代码,也能在面试中展现你的底层功底。

反编译工具

使用 javap -c 反编译 class 文件,查看字节码指令:

javac IfElseDemo.java
javap -c -p IfElseDemo

关键字节码指令

字节码指令操作数含义类比
ifeq偏移量栈顶 int 值等于 0 时跳转if (value == 0)
ifne偏移量栈顶 int 值不等于 0 时跳转if (value != 0)
iflt偏移量栈顶 int 值小于 0 时跳转if (value < 0)
ifge偏移量栈顶 int 值大于等于 0 时跳转if (value >= 0)
ifgt偏移量栈顶 int 值大于 0 时跳转if (value > 0)
ifle偏移量栈顶 int 值小于等于 0 时跳转if (value <= 0)
if_icmpeq偏移量比较栈顶两个 int 值,相等时跳转if (a == b)
if_icmpne偏移量比较栈顶两个 int 值,不等时跳转if (a != b)
if_icmplt偏移量比较栈顶两个 int 值,小于时跳转if (a < b)
if_icmpge偏移量比较栈顶两个 int 值,大于等于时跳转if (a >= b)
if_icmpgt偏移量比较栈顶两个 int 值,大于时跳转if (a > b)
if_icmple偏移量比较栈顶两个 int 值,小于等于时跳转if (a <= b)
if_acmpeq偏移量比较两个引用是否指向同一对象if (obj1 == obj2)
if_acmpne偏移量比较两个引用是否指向不同对象if (obj1 != obj2)
goto偏移量无条件跳转跳过 else 分支
ifnull偏移量引用为 null 时跳转if (obj == null)
ifnonnull偏移量引用不为 null 时跳转if (obj != null)

示例:简单 if-else 的字节码分析

Java 源码:

public static int max(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

使用 javap -c 反编译后的字节码:

public static int max(int, int);
  Code:
     0: iload_0            // 将参数 a 压入操作数栈
     1: iload_1            // 将参数 b 压入操作数栈
     2: if_icmple  7       // 比较 a 和 b,如果 a <= b 跳转到偏移量 7
     5: iload_0            // a > b:将 a 压栈准备返回
     6: ireturn            // 返回 a
     7: iload_1            // a <= b:将 b 压栈准备返回
     8: ireturn            // 返回 b

关键发现:

  1. 条件取反:Java 源码是 if (a > b),但字节码使用 if_icmple(a <= b 时跳转)。这是因为字节码的跳转逻辑是"条件满足时跳过 if 代码块",必须取反。
  2. goto 省略:当 if 和 else 都以 return 结束时,else 分支天然是 fall-through,不需要显式 goto。
  3. 性能提示:简单的 if-else 在 JIT 编译后可能被优化为条件传送指令(cmov),消除分支预测开销。

易错场景

反例一:误用 == 比较字符串

// ❌ 错误:用 == 比较字符串内容
String dept = new String("技术部");
if (dept == "技术部") {
    System.out.println("技术部员工");
}
// 输出:(无输出,因为 == 比较的是引用地址)

原因分析:== 比较的是栈中的引用值(内存地址),而不是堆中字符串的实际内容。new String("技术部") 创建了一个新对象,其地址与字符串常量池中的 "技术部" 不同。

// ✅ 正确:使用 equals() 比较内容
String dept = "技术部";
if ("技术部".equals(dept)) {
    System.out.println("明确是技术部员工");
}

反例二:条件顺序导致的"死分支"

小崔在写薪资判断逻辑时,不小心写出了永远不会被执行的 else if 分支:

// ❌ 错误:条件顺序不当,"死分支"永远不会执行
double salary = 50000;
if (salary > 10000) {
    System.out.println("高薪");
} else if (salary > 50000) {   // 永远不可能走到这里!
    System.out.println("超高薪"); // 死分支
}
// salary=50000 时先匹配第一个条件,打印"高薪"

纠正:多分支的条件应该从最严格或最特殊的开始排列。

// ✅ 正确:从大到小排列条件
double salary = 50000;
if (salary > 50000) {
    System.out.println("超高薪");
} else if (salary > 10000) {
    System.out.println("高薪");
} else {
    System.out.println("普通薪资");
}

反例一:省略花括号导致的歧义

// ❌ 错误:省略花括号,缩进具有误导性
int score = 85;
String grade = "B";
if (score >= 90)
    grade = "A";
    System.out.println("优秀");  // 这条语句不受 if 控制!
// 无论 score 是多少,都会打印"优秀"

C/C++ 程序员容易踩这个坑。Java 编译器不会报错——代码完全合法,只是逻辑错了。

// ✅ 正确:始终使用花括号
int score = 85;
String grade = "B";
if (score >= 90) {
    grade = "A";
    System.out.println("优秀");
}

反例一:浮点数直接比较

// ❌ 错误:浮点数直接比较可能因精度问题失败
double price = 0.1 + 0.2;  // 实际值:0.30000000000000004
if (price == 0.3) {
    System.out.println("价格正确");
}
// 输出:(无输出)
// ✅ 正确:使用容差比较
double price = 0.1 + 0.2;
double EPSILON = 1e-10;
if (Math.abs(price - 0.3) < EPSILON) {
    System.out.println("价格正确(在误差范围内)");
}

反例一:空指针在条件判断中

// ❌ 错误:未判空直接调用方法
String level = null;
if (level.equals("P5")) {   // NullPointerException!
    System.out.println("职级 P5");
}
// ✅ 正确:常量在前,或先判空
String level = null;
if ("P5".equals(level)) {   // 安全,返回 false 而非抛异常
    System.out.println("职级 P5");
}

// 或者
if (level != null && level.equals("P5")) {
    System.out.println("职级 P5");
}

面试考点

Q1:if-else if-else 的匹配原则是什么?else 与哪个 if 配对?

悬空 else 问题:else 总是与最近的、尚未配对的 if 配对(最近匹配原则)。编译器严格按代码结构匹配,不受缩进影响。

if (a > 0)
    if (b > 0)
        System.out.println("1");
else              // 这个 else 与 if (b > 0) 配对,而非 if (a > 0)
    System.out.println("2");

因此,永远使用花括号,即使只有一行代码。

Q2:if-else 和三元运算符(? :)如何选择?

如果两个分支都是给同一个变量赋值,优先使用三元运算符,代码更简洁:

// 推荐:三元运算符
String status = (score >= 60) ? "通过" : "未通过";

// 不推荐:if-else 做同样的事
String status;
if (score >= 60) {
    status = "通过";
} else {
    status = "未通过";
}

但三元运算符不可嵌套超过一层,否则严重降低可读性。

Q3:为什么 Java 的 if 条件必须返回 boolean,而 C 语言可以接受 int?

这是 Java 设计者有意为之——为了类型安全。在 C 语言中 if (1) 合法,if (a = 5)(赋值而非比较)也是合法的且极易引发 Bug。Java 强制要求 boolean 类型,从编译器层面杜绝了这类错误。

字节码层面:JVM 没有专门的 boolean 类型,boolean 在编译后被映射为 int(0 表示 false,1 表示 true),但编译器会强制执行类型检查。

Q4:if-else 和 switch 如何选择?

维度if-elseswitch
判断条件任意布尔表达式仅支持等值判断
分支数量适合少量分支(≤3)适合较多分支(≥4)
性能O(n) 线性判断tableswitch 实现 O(1)
可读性复杂条件时更好等值判断时更清晰

规则:条件为等值判断且分支数较多时用 switch,其余情况用 if-else。


小结

要点说明
if 条件必须是 boolean 类型不能用 int 或其它类型
多分支条件顺序很重要最特殊/最严格的条件排前面
始终使用花括号 {}避免悬空 else 和歧义
字符串比较用 equals()== 比较的是引用地址
嵌套 if 不要超过 3 层超过则考虑提前 return 或策略模式重构
字节码取反实现分支跳转理解 if_icmple 等指令有助于读懂反编译结果
上一页
常见的程序运行流程
下一页
switch 多分支选择