
选择语句这东西刚学编程的人觉得简单写起来才发现到处都是坑。我见过太多人把 if-else 写得像意大利面条也见过 switch 里忘了 break 导致整个逻辑跑偏。这篇内容就是围绕“选择语句的练习”这个主题把我在实际教学和项目代码审查中积累的经验整理出来。不管你是刚接触编程的新手还是想回头巩固基础的开发者这里面的练习思路、常见错误和排查技巧都能直接拿去用。选择语句是程序逻辑分支的核心练好它后面学循环、函数、面向对象都会顺畅很多。1. 选择语句练习的整体设计思路1.1 为什么选择语句值得反复练习很多人觉得选择语句不就是 if 和 switch 吗有什么好练的。但实际写代码的时候业务逻辑的复杂程度远超想象。一个电商系统的折扣计算可能涉及用户等级、活动类型、商品品类、时间段四个维度的组合判断写出来就是十几层嵌套。如果基础的选择语句练习没做到位遇到这种场景基本就是灾难。选择语句练习的核心目的不是记住语法而是训练三种能力第一是把自然语言的业务规则准确翻译成条件表达式的能力第二是处理边界情况和异常输入的能力第三是在多个方案之间做出合理取舍的能力。这三种能力光看教程是学不会的必须动手练。我在带新人的时候发现一个规律选择语句写得清楚的人后面学循环和函数也会很顺选择语句写得乱七八糟的人后面写什么都是一团麻。因为选择语句本质上是在训练“把复杂问题拆解成互斥分支”的思维方式这个思维方式是所有编程活动的基础。1.2 练习难度的阶梯式安排选择语句的练习不能一上来就搞复杂的嵌套得有个梯度。我一般把练习分成四个阶段单分支练习只有一个 if条件简单直接。比如判断一个数是否为正数。这个阶段的目标是熟悉基本语法和条件表达式的写法。双分支练习if-else 结构处理非此即彼的场景。比如判断奇偶数、判断闰年。这个阶段开始接触逻辑运算符。多分支练习if-else if-else 链或者 switch处理多个互斥条件。比如成绩等级划分、月份天数判断。这个阶段要开始注意条件的顺序和覆盖完整性。嵌套与组合练习多层嵌套或者选择语句与循环、函数的组合。比如根据用户输入的不同命令执行不同操作同时还要处理非法输入。这个阶段训练的是综合运用能力。每个阶段至少练 5 到 8 道题不要跳。我见过有人觉得自己看懂了就直接跳到嵌套结果写出来的代码条件覆盖不全测试的时候到处是漏洞。1.3 练习题材的选择原则选题材这件事很多人不重视随便找几道题做做就完了。但题材选得好不好直接影响练习效果。我的经验是遵循三个原则第一贴近真实场景。不要练那种“输入一个数判断它是不是 7 的倍数”这种纯数学题要练有业务背景的。比如“根据用户输入的会员等级和消费金额计算折扣”这种题目你会自然地去考虑等级有哪些、金额边界在哪里、折扣怎么叠加。第二条件维度逐步增加。一开始一个条件然后两个条件组合再然后三个条件。每增加一个维度复杂度是翻倍的练习效果也翻倍。第三包含异常处理。真实场景里用户输入永远是不可靠的练习的时候就要养成处理异常输入的习惯。比如输入了负数、输入了字母、输入了超出范围的值这些都要考虑到。2. 核心细节解析与实操要点2.1 条件表达式的写法与陷阱条件表达式是选择语句的灵魂写不好条件后面全白搭。先看几个常见的坑。浮点数比较。这是新手最容易踩的坑。你写if (a 0.1 0.2)结果永远是 false因为浮点数精度问题0.1 0.2 实际是 0.30000000000000004。正确的做法是用一个极小的误差范围来判断EPSILON 1e-9 if abs(a - 0.3) EPSILON: print(相等)字符串比较。不同语言里字符串比较的行为不一样。Java 里比较的是引用地址要用.equals()方法。Python 里比较的是内容但要注意大小写和空格。C 语言里字符串不能用得用strcmp()函数。这些细节不搞清楚写出来的代码逻辑就是错的。短路求值。大多数语言里和||是短路求值的也就是说a b如果 a 为 falseb 就不会执行。这个特性可以用来做空值保护if (user ! null user.getAge() 18) { // 安全user 为 null 时不会执行 getAge() }但反过来如果你把顺序写反了if (user.getAge() 18 user ! null)user 为 null 的时候直接空指针异常。这个顺序问题在实际项目中非常常见一定要养成“先判空再取值”的习惯。布尔表达式的简化。很多人喜欢写if (flag true)其实直接写if (flag)就行。还有if (a 0 a 100)可以写成if (0 a a 100)虽然功能一样但后者更符合数学直觉。这些细节看起来小但代码审查的时候会被挑。2.2 if-else 链的顺序问题if-else if-else 链最容易出问题的地方就是条件的顺序。看一个例子根据分数判断等级# 错误写法 if score 60: grade 及格 elif score 80: grade 良好 elif score 90: grade 优秀这个写法是错的因为 score 为 95 的时候第一个条件score 60就满足了直接返回“及格”后面的判断根本不会执行。正确的写法是从严格到宽松排列# 正确写法 if score 90: grade 优秀 elif score 80: grade 良好 elif score 60: grade 及格 else: grade 不及格这个原则叫做“先特殊后一般”。在实际业务中如果有多个条件可能同时满足一定要把最特殊、最严格的条件放在前面。比如计算运费VIP 用户免运费、满 99 包邮、偏远地区加收运费这三个条件的优先级和顺序需要仔细设计。注意if-else 链一定要有最终的 else 分支哪怕只是打印一个“未知情况”的提示。没有 else 的 if-else 链在代码审查中会被标记为“可能遗漏分支”。2.3 switch 语句的使用要点switch 适合处理“一个变量对应多个固定值”的场景比如根据星期几输出对应的活动、根据状态码返回不同的提示信息。但 switch 有几个硬性限制需要记住case 值必须是常量。不能是变量不能是表达式必须是编译期能确定的常量。Java 里还要求是整型、字符串或枚举Python 直到 3.10 才有 match-case 语法。break 不能忘。这是 switch 最经典的坑。忘了 break 会导致“穿透”也就是执行完当前 case 后继续执行下一个 case 的代码。有时候穿透是有意为之比如多个 case 共享同一段逻辑但大多数时候是 bug。// 有意的穿透多个 case 共享逻辑 switch (day) { case 1: case 2: case 3: case 4: case 5: System.out.println(工作日); break; case 6: case 7: System.out.println(周末); break; default: System.out.println(无效输入); }default 分支要写。和 if-else 链的 else 一样default 是兜底逻辑。即使你觉得所有情况都覆盖了也要写 default因为将来可能新增枚举值或者状态码。switch 和 if-else 的选择。判断条件超过 3 个且都是等值判断的时候switch 的可读性更好。但如果涉及范围判断比如大于、小于switch 就不合适了得用 if-else。2.4 嵌套选择语句的简化技巧嵌套是选择语句练习中最难的部分。三层以上的嵌套基本就没法看了维护起来是噩梦。我总结了几个简化技巧提前返回。把不满足条件的情况提前 return 或者 continue减少嵌套层级。比如# 嵌套写法 def process(user): if user is not None: if user.is_active(): if user.has_permission(): do_something() # 提前返回写法 def process(user): if user is None: return if not user.is_active(): return if not user.has_permission(): return do_something()合并条件。用逻辑运算符把多个条件合并成一个。if (a) { if (b) { ... } }可以写成if (a b) { ... }。提取函数。把复杂的条件判断提取成一个独立的函数函数名就是注释。比如if (isEligibleForDiscount(user, order))比一堆条件表达式清楚得多。用卫语句替代嵌套。卫语句就是先处理异常情况正常流程放在最后。这个技巧在函数入口处特别有用。3. 实操过程与核心环节实现3.1 练习一成绩等级判定系统这是最经典的选择语句练习但大多数人练得不够深入。我把它扩展成一个完整的成绩判定系统包含多个维度的判断。需求是这样的输入一个 0 到 100 的分数输出对应的等级。90 分以上为 A80 到 89 为 B70 到 79 为 C60 到 69 为 D60 以下为 F。同时要处理非法输入比如负数、超过 100 的数、非数字字符。def get_grade(score_str): # 第一步输入合法性检查 try: score float(score_str) except ValueError: return 错误请输入数字 # 第二步范围检查 if score 0 or score 100: return 错误分数必须在 0 到 100 之间 # 第三步等级判定从高到低排列 if score 90: return A elif score 80: return B elif score 70: return C elif score 60: return D else: return F这个练习的关键点有三个第一是输入类型转换的异常处理第二是范围检查第三是条件顺序。很多人只写了等级判定忽略了前两步实际项目中这就是 bug。我建议在这个基础上继续扩展加上小数点处理比如 89.5 算 B 还是 A、加上等级对应的评语、加上多个学生的批量处理。每加一个需求你就多练一次选择语句的组合。3.2 练习二简单的计算器计算器的练习价值在于它涉及多分支和运算符判断。需求输入两个数和运算符输出计算结果。要处理除零错误和非法运算符。def calculate(num1_str, num2_str, operator): try: num1 float(num1_str) num2 float(num2_str) except ValueError: return 错误请输入有效数字 if operator : return num1 num2 elif operator -: return num1 - num2 elif operator *: return num1 * num2 elif operator /: if abs(num2) 1e-9: return 错误除数不能为零 return num1 / num2 else: return 错误不支持的运算符这个练习的进阶版本是用 switch 或者 match-case 来写对比两种写法的可读性。还可以加上连续计算的功能也就是把上一次的结果作为下一次的输入这就涉及循环和选择语句的组合了。提示除零判断不要用num2 0浮点数运算中要用误差范围判断。这是实际项目中很容易忽略的细节。3.3 练习三闰年判断闰年判断看起来简单但条件组合很有代表性。规则是能被 4 整除但不能被 100 整除的年份是闰年或者能被 400 整除的年份也是闰年。def is_leap_year(year): if (year % 4 0 and year % 100 ! 0) or (year % 400 0): return True else: return False这个练习的价值在于训练逻辑运算符的组合使用。你可以进一步扩展给定年份和月份输出该月有多少天。这就涉及闰年判断和月份判断的组合需要嵌套或者多个条件。def days_in_month(year, month): if month 1 or month 12: return 错误月份必须在 1 到 12 之间 if month in [1, 3, 5, 7, 8, 10, 12]: return 31 elif month in [4, 6, 9, 11]: return 30 else: # month 2 if is_leap_year(year): return 29 else: return 28这个扩展练习把选择语句的嵌套和多分支都练到了而且逻辑清晰适合反复琢磨。3.4 练习四用户登录权限判断这个练习模拟真实的业务场景。需求用户输入用户名和密码系统根据用户角色管理员、普通用户、访客和账号状态正常、锁定、过期决定是否允许登录以及登录后的权限。def login(username, password, role, status): # 先检查账号状态 if status locked: return 账号已锁定请联系管理员 elif status expired: return 账号已过期请续费 elif status ! active: return 未知账号状态 # 再检查角色权限 if role admin: return 登录成功拥有全部权限 elif role user: return 登录成功拥有基本权限 elif role guest: return 登录成功仅可浏览 else: return 未知角色拒绝登录这个练习的关键在于条件的优先级设计。账号状态的检查要放在角色检查之前因为账号锁定了什么角色都没用。这种优先级思维在实际业务中非常重要。进阶版本可以加上密码错误次数限制、登录时间限制比如只能在 9 点到 18 点登录、IP 白名单判断等。每加一个维度选择语句的复杂度就上一个台阶。3.5 练习五运费计算器电商场景的运费计算是选择语句的经典应用。需求根据用户等级普通、VIP、SVIP、订单金额、配送地区本地、外地、偏远计算运费。规则如下VIP 和 SVIP 免运费普通用户订单满 99 免运费偏远地区无论什么等级都加收 20 元外地加收 10 元本地不加收。def calculate_shipping(user_level, order_amount, region): # 基础运费 base 0 # 地区附加费 if region remote: base 20 elif region non_local: base 10 elif region ! local: return 错误未知地区 # 用户等级和订单金额判断 if user_level in [VIP, SVIP]: return base # 免基础运费但地区附加费照收 elif user_level normal: if order_amount 99 and region ! remote: return base # 满 99 免运费偏远地区除外 else: return base 10 # 基础运费 10 元 else: return 错误未知用户等级这个练习的难点在于多个条件的组合和优先级。VIP 免运费但偏远地区附加费照收普通用户满 99 免运费但偏远地区除外。这些规则在真实业务中很常见练习的时候要把每种组合都测试一遍。4. 常见问题与排查技巧实录4.1 条件覆盖不全导致逻辑漏洞这是选择语句最常见的 bug。比如写了一个 if-else if 链但漏掉了某个区间导致某些输入没有任何分支处理。排查方法很简单把所有可能的输入范围列出来逐一检查是否有对应的分支。我一般用等价类划分的方法来检查把输入分成若干个等价类每个等价类选一个代表值测试。比如成绩判定的输入可以分成负数、0 到 59、60 到 69、70 到 79、80 到 89、90 到 100、超过 100、非数字。每个类都要测试。输入类型测试值预期结果实际结果负数-10错误提示0-5945F60-6965D70-7975C80-8985B90-10095A超过 100105错误提示非数字abc错误提示这个表格在测试的时候非常有用每改一次代码就重新跑一遍确保没有回归问题。4.2 浮点数比较的隐蔽 bug浮点数比较的问题很隐蔽因为大多数时候看起来是对的只有在特定值上才会出错。比如0.1 0.2 0.3返回 false但0.5 0.5 1.0返回 true。这种不确定性在金融计算、科学计算中会导致严重问题。解决方案有三种第一种是用误差范围判断适合大多数场景第二种是用整数运算代替浮点运算比如金额用分而不是元第三种是用专门的十进制库比如 Python 的decimal模块。from decimal import Decimal a Decimal(0.1) Decimal(0.2) b Decimal(0.3) print(a b) # True注意涉及金额计算的时候永远不要用浮点数。这是血泪教训我见过因为浮点数精度问题导致对账差几分钱的案例。4.3 switch 穿透的排查与利用switch 穿透是双刃剑用好了可以简化代码用不好就是 bug。排查穿透问题的方法是在每个 case 末尾检查是否有 break如果没有确认是否是有意为之。如果是有意的加注释说明。switch (status) { case pending: case processing: // 这两种状态都显示处理中 showProcessing(); break; case completed: showCompleted(); break; default: showUnknown(); }我个人的习惯是除非多个 case 共享同一段逻辑否则每个 case 都加 break。有意的穿透一定要加注释不然代码审查的时候会被问。4.4 嵌套过深的代码重构嵌套超过三层的时候代码基本就没法维护了。重构的方法前面提过提前返回、合并条件、提取函数。这里再补充一个技巧用查表法替代条件判断。比如根据月份输出季节用 if-else 要写 12 个分支用查表法就很简单season_map { 1: 冬, 2: 冬, 3: 春, 4: 春, 5: 春, 6: 夏, 7: 夏, 8: 夏, 9: 秋, 10: 秋, 11: 秋, 12: 冬 } season season_map.get(month, 未知)查表法适合“输入值到输出值的直接映射”场景比 if-else 和 switch 都简洁。但如果是范围判断或者复杂条件还是得用选择语句。4.5 常见问题速查表问题现象可能原因排查方法解决方案某些输入没有输出条件覆盖不全等价类划分测试补全分支或加 else结果与预期不符条件顺序错误检查条件优先级从严格到宽松排列浮点数比较失败精度问题打印实际值用误差范围或 Decimalswitch 执行了多个分支忘了 break检查每个 case加 break 或注释说明空指针异常条件顺序问题检查短路求值顺序先判空再取值代码嵌套太深缺少提前返回数嵌套层数用卫语句重构4.6 实操心得与避坑建议最后分享几条我在实际项目中总结的经验。第一条先画流程图再写代码。选择语句的逻辑分支多了以后光在脑子里想容易漏。拿张纸把流程图草稿画出来每个分支标清楚条件和结果然后再翻译成代码。这个习惯能减少一半以上的逻辑错误。第二条每个分支都要能独立测试。写选择语句的时候要想着“我怎么单独测试这个分支”。如果一个分支很难单独触发说明条件设计有问题可能需要重构。第三条边界值重点测试。条件的边界值是最容易出错的地方。和差一个等号结果完全不同。测试的时候要把每个边界的值、边界减一、边界加一都测一遍。第四条不要怕写 else。有些人觉得所有情况都覆盖了就不写 else但将来需求变了、枚举加了新值没有 else 就会出问题。else 里哪怕只是抛一个异常或者打一条日志也比没有强。第五条条件表达式要短。一个条件表达式超过 80 个字符就很难阅读了。如果条件太长提取成布尔变量或者函数。比如if (user.getAge() 18 user.getCountry() CN user.getStatus() active)可以改成boolean canVote user.getAge() 18 user.getCountry() CN user.getStatus() active; if (canVote) {...}。第六条多用括号。逻辑运算符的优先级很容易记混的优先级高于||但很多人记不住。与其纠结优先级不如直接加括号。if ((a || b) c)比if (a || b c)清楚得多虽然功能可能一样但可读性差很多。第七条switch 的 default 不要省。即使你觉得所有情况都覆盖了default 也是必要的。它是对未来的保护也是对代码健壮性的保证。第八条练习的时候刻意增加难度。做完基础题之后自己给自己加需求。比如成绩判定加一个“加分项”运费计算加一个“优惠券”维度登录判断加一个“验证码”检查。每加一个维度你就多练一次选择语句的组合和优先级设计。选择语句的练习不是做一遍就完了要反复做、变着花样做。同一个题目用 if-else 写一遍用 switch 写一遍用查表法写一遍对比三种写法的优劣。这个过程能让你真正理解选择语句的本质而不是停留在语法层面。我在实际项目中遇到复杂业务逻辑的时候脑子里会自动浮现出几种不同的选择语句写法然后根据可读性、可维护性、性能等因素选择最合适的一种。这种直觉就是靠大量练习积累出来的。