Java基础(五):流程控制全解析——分支(if/switch)和循环(for/while)的深度指南
Java基础系列文章
Java基础(一):发展史、技术体系与JDK环境配置详解
Java基础(二):八种基本数据类型详解
Java基础(三):逻辑运算符详解
Java基础(四):位运算符详解
Java基础(五):if/switch与for/while - 深入理解流程控制
目录
- 引言
- 一、分支结构
-
- 1、if语句
- 2、if-else语句
- 3、if-else-if阶梯
- 4、switch语句 (传统与现代)
-
- 4.1、传统语法 (Java 7 及之前,注意break)
- 4.2、现代语法 (Java 12+, 使用->箭头和yield)
- 4.3、更高级语法 (Java 17+, 模式匹配预览特性)
- 二、循环结构
-
- 1、for循环
- 2、while循环
- 3、do-while循环
- 4、增强for循环 (for-each)
- 三、流程控制的利器:break和continue
-
- 1、break语句
- 2、continue语句
- 3、带标签的break和continue(谨慎使用)
- 四、无限循环与空语句
-
- 1、无限循环
- 2、空语句
- 总结
引言
流程控制
是编程语言的基石,它决定了代码执行的顺序
和路径
。Java提供了强大而灵活的流程控制语句,使你能够根据条件执行代码、重复执行任务以及跳出循环。掌握这些结构是写出高效、逻辑清晰程序的关键。
一、分支结构
1、if语句
- 语法:
if (condition) { // 当 condition 为 true 时执行的代码块}
- 执行流程:
- 计算
condition
(必须是一个布尔表达式:true
或false
) - 如果结果为
true
,则执行代码块内的语句 - 如果结果为
false
,则跳过代码块,继续执行后面的代码
- 计算
- 示例:
int age = 18;if (age >= 18) { System.out.println(\"你已成年。\");}// 无论 age 是否 >=18, 这行都会执行System.out.println(\"年龄检查完毕。\");
2、if-else语句
- 语法:
if (condition) { // 当 condition 为 true 时执行的代码块} else { // 当 condition 为 false 时执行的代码块}
- 执行流程:
- 计算
condition
- 如果
true
,执行if
代码块 - 如果
false
,执行else
代码块 - 两个代码块
必选其一
执行,且只执行其一
- 计算
- 示例:
int score = 75;if (score >= 60) { System.out.println(\"及格!\");} else { System.out.println(\"不及格!\");}
3、if-else-if阶梯
- 语法:
if (condition1) { // condition1 为 true 时执行} else if (condition2) { // condition1 为 false 且 condition2 为 true 时执行} else if (condition3) { // 前面条件都为 false 且 condition3 为 true 时执行} else { // 所有条件都为 false 时执行 (可选)}
- 执行流程:
- 按顺序检查每个
condition
- 遇到第一个为
true
的condition
,则执行其对应的代码块,然后跳过整个if-else-if
结构剩余部分 - 如果所有
condition
都为false
,则执行else
块(如果存在);如果不存在else
,则整个结构不执行任何操作
- 按顺序检查每个
- 示例:
int grade = 85;if (grade >= 90) { System.out.println(\"优秀 (A)\");} else if (grade >= 80) { // 隐含 grade < 90 System.out.println(\"良好 (B)\");} else if (grade >= 70) { // 隐含 grade < 80 System.out.println(\"中等 (C)\");} else if (grade >= 60) { // 隐含 grade < 70 System.out.println(\"及格 (D)\");} else { System.out.println(\"不及格 (F)\");}
4、switch语句 (传统与现代)
基于一个表达式
的值,从多个可能的执行路径中选择一个。适用于多分支选择,尤其当分支基于单个变量或表达式的离散值(整数、字符、字符串 String
、枚举 enum
)时,通常比 if-else-if
更清晰。
4.1、传统语法 (Java 7 及之前,注意break)
switch (expression) { case value1: // expression 等于 value1 时执行的语句 break; // 跳出 switch 块 case value2: // expression 等于 value2 时执行的语句 break; ... // 可以有任意多个 case default: // 如果 expression 不匹配任何 case 值时执行的语句 (可选)}
expression
:可以是byte
,short
,int
,char
(Java 7之前),以及String
(Java 7+),enum
case valueN
:valueN
必须是常量表达式,且类型必须与expression
兼容。每个case
代表一个可能匹配的值break
:至关重要!它终止当前case
的执行并跳出整个switch
块。如果省略break
,程序会继续执行下一个case
中的语句(无论其值是否匹配),这称为“case穿透(fall-through)”
。除非有意设计穿透,否则必须写break
default
:可选的。当没有case
匹配时执行。可以放在任何位置(开头、中间、结尾),但通常放结尾。不需要break
(如果它是最后一个)- 示例 (传统带break):
int dayOfWeek = 3;switch (dayOfWeek) { 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; default: System.out.println(\"周末或无效日期\");} // 输出: 星期三
- 示例 (故意 Case 穿透):
char grade = \'B\';switch (grade) { case \'A\': case \'B\': // A 或 B 都执行下面的代码 System.out.println(\"成绩优良\"); break; case \'C\': System.out.println(\"成绩中等\"); break; case \'D\': case \'F\': // D 或 F 都执行下面的代码 System.out.println(\"需要努力\"); break; default: System.out.println(\"无效成绩\");} // 输出: 成绩优良
4.2、现代语法 (Java 12+, 使用->箭头和yield)
Java 12
引入了更简洁、更安全(避免意外穿透)的 switch
表达式和语句形式::
-
箭头标签 (
case L ->
):使用->
代替:
。如果标签匹配,则只执行->
右侧的表达式或语句块 -
多值匹配 (
case L1, L2 ->
):一个case
可以匹配多个值,用逗号分隔 -
switch
表达式 (返回值):整个switch
可以作为一个表达式,使用yield
返回一个值。必须覆盖所有可能情况(或default
) -
示例 (现代
switch
语句,无穿透):int dayOfWeek = 3;switch (dayOfWeek) { case 1 -> System.out.println(\"星期一\"); // 单条语句可直接写 case 2 -> System.out.println(\"星期二\"); case 3 -> { // 多条语句用代码块 {} System.out.println(\"星期三\"); System.out.println(\"一周的中点!\"); } case 4, 5 -> System.out.println(\"临近周末\"); // 匹配 4 或 5 default -> System.out.println(\"周末或无效日期\");} // 输出: 星期三 \\n 一周的中点!
-
示例 (
switch
表达式,使用yield
返回值,类似于return
):int dayOfWeek = 3;String dayType = switch (dayOfWeek) { case 1, 2, 3, 4, 5 -> \"工作日\"; case 6, 7 -> { // 代码块中使用 yield 返回值 System.out.println(\"放假了...\"); yield \"周末\"; // yield 提供 switch 表达式的返回值 } default -> { // 代码块中使用 yield 返回值 System.out.println(\"无效输入: \" + dayOfWeek); yield \"未知\"; // yield 提供 switch 表达式的返回值 }}; // 注意:作为表达式,末尾有分号 \';\'System.out.println(dayOfWeek + \" 是 \" + dayType); // 输出: 3 是 工作日
4.3、更高级语法 (Java 17+, 模式匹配预览特性)
Java 17 开始引入对 switch 语句的新特性模式匹配
,作为预览功能
提供。这种特性允许在 switch 的 case 分支中直接进行类型判断
和绑定变量
,从而让代码更简洁、更安全。
static String test(Object obj) { return switch (obj) { case String s -> \"字符串: \" + s; case Integer i -> \"整数: \" + i; case null -> \"空值\"; default -> \"其他类型\"; };}
- case String s -> …:如果 obj 是一个字符串,就匹配并将其绑定到变量 s
- case Integer i -> …:同理,匹配整数
- case null -> …:甚至可以对 null 值进行单独处理
- default -> …:兜底处理其他类型
注意⚠️:这是预览功能:意味着它还不是 Java 标准的一部分,将来可能会改动,从 Java 21 开始,这一功能有望正式成为标准
二、循环结构
1、for循环
- 用途:当
循环次数已知
或在循环前就知道初始化、条件和迭代步骤
时特别适用。结构清晰 - 传统语法:
for (initialization; condition; iteration) { // 循环体:当 condition 为 true 时重复执行的代码}
initialization (初始化)
:在循环开始前执行一次
。通常用于声明和初始化循环控制变量 (如:int i = 0;
)condition (条件)
:每次循环迭代前
检查的布尔表达式。如果true
,执行循环体;如果false
,终止循环iteration (迭代/步进)
:每次循环体执行后
执行。通常用于更新循环控制变量 (如:i++
,i = i + 2
)
- 执行流程:
- 执行
initialization
(仅一次) - 计算
condition
- 如果
condition
为true
:- 执行循环体
- 执行
iteration
- 回到步骤 2 (再次检查
condition
)
- 如果
condition
为false
,循环终止
- 执行
- 示例:
// 打印 0 到 9for (int i = 0; i < 10; i++) { System.out.println(i);}// 计算 1 到 100 的和int sum = 0;for (int num = 1; num <= 100; num++) { sum += num;}System.out.println(\"Sum: \" + sum);
- 格式的多样性(
多变量控制
、省略部分语句
)// 省略部分语句(但必须保留分号)int i = 0;for ( ; i < 5; i++) { System.out.println(i);}// 多个变量用逗号隔开for (int i = 0, j = 10; i < j; i++, j--) { System.out.println(\"i = \" + i + \", j = \" + j);}
2、while循环
- 用途:当
循环次数未知
,但在循环开始前有一个明确的继续条件
时适用。先判断条件,再决定是否执行循环体 - 语法:
while (condition) { // 循环体:当 condition 为 true 时重复执行的代码}
- 执行流程:
- 计算
condition
- 如果
condition
为true
:- 执行循环体
- 回到步骤 1 (再次检查
condition
)
- 如果
condition
为false
,循环终止
- 计算
- 特点:循环体可能
一次都不执行
(如果初始条件就是false
) - 示例:
int i = 0;while (i < 5) { System.out.println(i); i++;}
3、do-while循环
- 用途:当
循环体至少需要执行一次
,然后再根据条件决定是否继续时适用。先执行一次循环体,再判断条件 - 语法:
do { // 循环体:至少执行一次} while (condition); // 注意结尾的分号
- 执行流程:
- 执行循环体
- 计算
condition
- 如果
condition
为true
,回到步骤 1 - 如果
condition
为false
,循环终止
- 特点:循环体
至少执行一次
- 示例:
// 1.基本用法int i = 0;do { System.out.println(i); i++;} while (i < 5);// 2.条件为假也执行一次int i = 10;do { System.out.println(\"i = \" + i); // 会执行一次 i++;} while (i < 5);
4、增强for循环 (for-each)
- 用途:专门用于
遍历数组
和实现了 Iterable 接口的集合
(如List
,Set
,Queue
)。语法简洁,避免了显式索引操作 - 语法:
for (ElementType element : collectionOrArray) { // 使用 element 执行操作}
ElementType
:集合或数组中元素的类型
element
:循环变量
,在每次迭代中依次被赋值为集合或数组中的当前元素collectionOrArray
:要遍历的数组
或集合
对象
- 执行流程:自动依次从数组或集合中取出每个元素,赋值给
element
,并执行循环体。遍历完所有元素后结束 优点
:简洁、安全(无需管理索引)、不易出错(避免索引越界)缺点
:不能直接访问当前元素的索引;不能修改集合结构(如删除元素,否则可能引发ConcurrentModificationException
);只能单向顺序遍历- 示例:
// 1.遍历数组int[] numbers = {1, 2, 3, 4, 5};for (int num : numbers) { System.out.print(num + \" \");} // 输出: 1 2 3 4 5// 2.遍历 List 集合List<String> fruits = Arrays.asList(\"Apple\", \"Banana\", \"Orange\");for (String fruit : fruits) { System.out.println(fruit);} // 输出: Apple \\n Banana \\n Orange
三、流程控制的利器:break和continue
1、break语句
- 用途:
- 在循环中 (
for
,while
,do-while
):立即终止
其所在的最内层循环,跳出循环体,继续执行循环之后的代码 - 在
switch
中:终止case
的执行并跳出整个switch
块(防止穿透)
- 在循环中 (
- 示例 (跳出循环):
// 查找数组中第一个负数int[] nums = {5, 8, -2, 10, 3};boolean found = false;for (int num : nums) { if (num < 0) { System.out.println(\"找到负数: \" + num); found = true; break; // 找到第一个负数后立即跳出循环 }}if (!found) { System.out.println(\"没有找到负数\");}
2、continue语句
- 用途:仅用于
循环中
。跳过当前迭代
中循环体内continue
语句之后
的所有代码,立即进入下一次迭代
(检查循环条件并执行步进语句) - 示例:
// 打印 1 到 10 的奇数for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { // 如果是偶数 continue; // 跳过本次循环剩余部分,直接 i++ 然后检查 i<=10 } System.out.println(i); // 只有奇数会执行到这里}// 输出: 1 \\n 3 \\n 5 \\n 7 \\n 9
3、带标签的break和continue(谨慎使用)
- 用途:用于跳出
多层嵌套
的循环或语句块 - 语法:在目标循环或语句块
前
放置一个标签 (label:
), 然后在break
或continue
后指定该标签名 (break label;
/continue label;
) break label;
:立即终止标签所标记的整个
循环或语句块-
continue label;
:立即跳转到标签所标记的循环的下一次迭代
开始处(跳过标记循环当前迭代的剩余部分) - 示例 (跳出外层循环):
outerLoop: // 标签定义在外层 for 循环前for (int i = 0; i < 5; i++) { innerLoop: // 标签定义在内层 for 循环前 for (int j = 0; j < 5; j++) { System.out.println(\"i=\" + i + \", j=\" + j); if (i == 2 && j == 2) { break outerLoop; // 跳出整个 outerLoop (两层循环都终止) } }}// 当 i=2, j=2 时输出终止,后续 i=3,4 都不再执行
注意
:带标签的break/continue会破坏代码结构
,使逻辑不易追踪。优先考虑重构代码(如将内层循环提取为方法
)来避免使用它们。仅在逻辑清晰且必要时使用。
四、无限循环与空语句
1、无限循环
- 定义:循环条件始终为
true
的循环,理论上会一直执行下去 - 常见形式:
// while 形式while (true) { // ... 需要某种 break 条件跳出 ...}// for 形式for (;;) { // 初始化、条件、迭代都省略 // ... 需要某种 break 条件跳出 ...}
- 用途:服务器监听、游戏主循环、需要用户主动退出的程序等。必须在循环体内提供跳出机制(如
break
,return
,System.exit()
) - 风险:如果缺少跳出机制,程序将永远挂起,消耗CPU资源
2、空语句
- 定义:一个单独的分号
;
表示一条不执行任何操作的语句 - 在流程控制中的潜在陷阱:
// 意图:i<10 时才打印 iint i = 0;while (i < 10); // 注意这里错误地多了一个分号!这是一个空语句循环体。{ System.out.println(i); // 这行代码在 while 循环块之外! i++;}// 结果:while (i < 10); 是一个无限循环(如果 i<10),因为循环体是空语句 `;`。// { ... } 只是一个普通的代码块,在无限循环之后(永远执行不到)。
警示
:在if
,for
,while
的条件后切勿随意加分号;
,除非你明确需要一个空循环体。这通常是逻辑错误
总结
Java的流程控制语句(分支:if
, switch
;循环:for
, while
, do-while
, for-each
;跳转:break
, continue
)为你提供了构建程序逻辑的完整工具箱。理解每种结构的语法、执行流程、适用场景和潜在陷阱,并结合清晰编码的最佳实践,是编写健壮、高效、易于维护的Java程序的基础。