流程控制
约 5956 字大约 20 分钟
2025-07-13
在 Java 程序中,JVM 默认总是顺序执行以分号 ;
结束的语句。但是,在实际的代码中,程序经常需要做条件判断、循环,因此,需要有多种流程控制语句,来实现程序的跳转和循环等功能。
本节我们将介绍 if
条件判断、switch
多重选择和各种循环语句。
4.1 输入和输出
在 Java 编程中,输入和输出是程序与用户交互以及处理数据的关键组成部分。本章主要介绍 Java 提供的各种输入和输出方法,重点包括格式化输出以及如何使用 Scanner
对象进行输入。
4.1.1 输出
在 Java 中,我们经常使用 System.out.println()
方法将信息输出到屏幕。println
是 “print line” 的缩写,表示输出内容后进行换行。如果希望输出后不换行,可以使用 System.out.print()
方法。
// 输出
public class Main {
public static void main(String[] args) {
System.out.print("A,");
System.out.print("B,");
System.out.print("C.");
System.out.println();
System.out.println("END");
}
}
上述代码的执行结果是在同一行输出 “A,B,C.”,然后换行,再输出 “END”。
4.1.2 格式化输出
Java 提供了格式化输出的功能,通过 System.out.printf()
方法实现。这种方法允许开发者按照特定的格式输出数据,使得输出结果更易于阅读和理解。
例如,直接输出一个较大的 double
类型数值时,可能会显示为科学计数法的形式,如下所示:
// 格式化输出
public class Main {
public static void main(String[] args) {
double d = 12900000;
System.out.println(d); // 1.29E7
}
}
为了以更友好的方式显示数据,可以使用 printf()
方法进行格式化输出。printf()
方法使用占位符 %?
来指定输出数据的格式。
// 格式化输出
public class Main {
public static void main(String[] args) {
double d = 3.1415926;
System.out.printf("%.2f\n", d); // 显示两位小数3.14
System.out.printf("%.4f\n", d); // 显示4位小数3.1416
}
}
在上述代码中,%.2f
和 %.4f
分别表示保留两位小数和四位小数的浮点数。
下表列出了一些常用的占位符及其说明:
占位符 | 说明 |
---|---|
%d | 格式化输出整数 |
%x | 格式化输出十六进制整数 |
%f | 格式化输出浮点数 |
%e | 格式化输出科学计数法表示的浮点数 |
%s | 格式化字符串 |
需要注意的是,由于 %
本身是占位符,因此要输出 %
字符本身,需要使用 %%
。
占位符还可以包含更详细的格式化参数,例如,将一个整数格式化为十六进制,并用 0 补足 8 位:
// 格式化输出
public class Main {
public static void main(String[] args) {
int n = 12345000;
System.out.printf("n=%d, hex=%08x", n, n); // 注意,两个%占位符必须传入两个数
}
}
上述代码中,%08x
表示将整数 n
格式化为十六进制,并用 0 补足 8 位。
更详细的格式化参数可以参考 JDK 文档 java.util.Formatter。
4.1.3 输入
相对于输出,Java 的输入要复杂一些。Java 提供了 Scanner
类来方便地从标准输入流(System.in
)读取数据。
下面是一个从控制台读取一个字符串和一个整数的例子:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建 Scanner 对象
System.out.print("Input your name: "); // 打印提示
String name = scanner.nextLine(); // 读取一行输入并获取字符串
System.out.print("Input your age: "); // 打印提示
int age = scanner.nextInt(); // 读取一行输入并获取整数
System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
}
}
代码解释:
- 导入
Scanner
类:import java.util.Scanner;
语句用于导入Scanner
类,这样才能在程序中使用它。import
语句必须放在 Java 源代码的开头。 - 创建
Scanner
对象:Scanner scanner = new Scanner(System.in);
创建了一个Scanner
对象,并将标准输入流System.in
作为参数传递给它。System.in
代表标准输入流,通常是键盘输入。 - 读取输入:
scanner.nextLine()
用于读取用户输入的一行字符串。scanner.nextInt()
用于读取用户输入的一个整数。Scanner
会自动将输入的数据转换为对应的类型。
- 格式化输出:
System.out.printf("Hi, %s, you are %d\n", name, age);
使用格式化输出将读取到的用户名和年龄输出到控制台。
要运行这段代码,需要先使用 javac Main.java
命令编译 Java 文件,然后使用 java Main
命令执行编译后的 class 文件。在程序运行时,会提示用户输入姓名和年龄,然后程序会格式化输出问候语。
好的,下面是对您提供的 Java 课件内容进行整理后的详细笔记。
4.2 if
语句详解
在 Java 编程中,if
语句是实现条件判断的核心工具。它允许程序根据特定条件的结果(true
或 false
)来决定是否执行某段代码。
4.2.1 if
语句的基本语法
if
语句的基本结构如下:
if (条件) {
// 条件为 true 时执行的代码块
}
JVM 会对 if
后的条件表达式进行计算,如果结果为 true
,则执行 if
语句块中的代码;否则,跳过该代码块。
以下是一个简单的示例:
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 60) {
System.out.println("及格了");
}
System.out.println("END");
}
}
这段代码首先声明一个整数变量 n
并赋值为 70。然后,if
语句判断 n
是否大于等于 60。由于条件成立(true
),程序将输出 " 及格了 ",接着输出 "END"。
if
语句块可以包含多条语句,用花括号 {}
括起来。例如:
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 60) {
System.out.println("及格了");
System.out.println("恭喜你");
}
System.out.println("END");
}
}
如果 if
语句块只包含一条语句,则可以省略花括号 {}
,但这通常不被推荐,因为它可能导致代码可读性下降和潜在的错误,尤其是在后续需要添加更多语句时。例如:
public class Main {
public static void main(String[] args) {
int n = 50;
if (n >= 60)
System.out.println("及格了");
System.out.println("恭喜你"); // 实际上这行代码不在 if 语句块内
System.out.println("END");
}
}
在这个例子中,尽管有缩进,但实际上只有 System.out.println("及格了");
属于 if
语句块,而 System.out.println("恭喜你");
会在任何情况下都执行。
4.3.2 else
语句
if
语句可以与 else
语句结合使用,用于在条件为 false
时执行另一段代码。
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 60) {
System.out.println("及格了");
} else {
System.out.println("挂科了");
}
System.out.println("END");
}
}
在这个例子中,如果 n
大于等于 60,则输出 " 及格了 ";否则,输出 " 挂科了 "。else
语句是可选的。
可以使用多个 if ... else if ... else
结构来实现多重条件判断。
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 90) {
System.out.println("优秀");
} else if (n >= 60) {
System.out.println("及格了");
} else {
System.out.println("挂科了");
}
System.out.println("END");
}
}
在这个例子中,程序首先判断 n
是否大于等于 90,如果是,则输出 " 优秀 ";否则,判断 n
是否大于等于 60,如果是,则输出 " 及格了 ";如果以上条件都不满足,则输出 " 挂科了 "。
需要特别注意的是,多重条件判断的顺序非常重要。例如,如果将上述代码改为:
public class Main {
public static void main(String[] args) {
int n = 100;
if (n >= 60) {
System.out.println("及格了");
} else if (n >= 90) {
System.out.println("优秀");
} else {
System.out.println("挂科了");
}
}
}
那么,当 n
等于 100 时,程序会输出 " 及格了 ",因为第一个条件 n >= 60
已经满足,后续的 else if
不再执行。因此,应该按照判断范围从大到小或从小到大的顺序进行判断,以确保逻辑正确。
4.3.3 浮点数比较
由于浮点数在计算机中无法精确表示,因此不能直接使用 ==
运算符判断两个浮点数是否相等。正确的做法是判断它们的差值是否小于某个临界值。
public class Main {
public static void main(String[] args) {
double x = 1 - 9.0 / 10;
if (Math.abs(x - 0.1) < 0.00001) {
System.out.println("x is 0.1");
} else {
System.out.println("x is NOT 0.1");
}
}
}
4.3.4 引用类型相等判断
对于引用类型,==
运算符判断的是两个变量是否指向同一个对象,而不是对象的内容是否相等。要判断两个引用类型变量的内容是否相等,必须使用 equals()
方法。
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
if (s1.equals(s2)) {
System.out.println("s1 equals s2");
} else {
System.out.println("s1 not equals s2");
}
}
}
在这个例子中,s1
和 s2
的内容都是 "hello",因此 s1.equals(s2)
返回 true,程序会输出 "s1 equals s2"。在大多数情况下(即排除: toLowerCase()
方法内部可能进行了优化,直接返回了常量池中的引用,而不是创建一个全新的对象),即使 s1
和 s2
的内容相同,s2
仍然是一个新创建的 String 对象,而不是字符串常量池中的引用。 因此,s1 == s2
的结果为 false
。
需要注意的是,如果 s1
为 null
,则 s1.equals(s2)
会抛出 NullPointerException
。为了避免这种情况,可以使用短路运算符 &&
或将字面量放在前面。
public class Main {
public static void main(String[] args) {
String s1 = null;
if (s1 != null && s1.equals("hello")) {
System.out.println("hello");
}
if ("hello".equals(s1)) {
// ...
}
}
}
4.3 Switch 多重选择
4.3.1 Switch 语句的基本用法
switch
语句基于表达式的结果跳转到匹配的 case
,并执行后续语句,直到遇到 break
结束。
// switch 语句示例
public class Main {
public static void main(String[] args) {
int option = 1;
switch (option) {
case 1:
System.out.println("Selected 1");
break;
case 2:
System.out.println("Selected 2");
break;
case 3:
System.out.println("Selected 3");
break;
}
}
}
代码解释:
- 这段代码展示了
switch
语句的基本结构。根据option
变量的值,程序会跳转到相应的case
执行。 break
语句用于防止 " 穿透 " 效应,确保每个case
只执行其对应的代码块。
如果没有任何 case
匹配,则执行 default
分支(如果存在)。
// 包含 default 的 switch 语句示例
public class Main {
public static void main(String[] args) {
int option = 99;
switch (option) {
case 1:
System.out.println("Selected 1");
break;
case 2:
System.out.println("Selected 2");
break;
case 3:
System.out.println("Selected 3");
break;
default:
System.out.println("Selected other");
break;
}
}
}
代码解释:
- 当
option
的值不匹配任何case
时,default
分支会被执行。
对于多个 ==
判断的情况,使用 switch
结构通常比 if...else if
语句更清晰。
// switch 语句等价的 if 语句
if (option == 1) {
System.out.println("Selected 1");
} else if (option == 2) {
System.out.println("Selected 2");
} else if (option == 3) {
System.out.println("Selected 3");
} else {
System.out.println("Selected other");
}
4.3.2 Switch 语句的注意事项
Case 穿透:
case
语句没有花括号{}
,且具有 " 穿透性 "。如果漏写break
,会导致执行完一个case
后,继续执行后续case
的代码。// 演示 case 穿透的示例 public class Main { public static void main(String[] args) { int option = 2; switch (option) { case 1: System.out.println("Selected 1"); case 2: System.out.println("Selected 2"); case 3: System.out.println("Selected 3"); default: System.out.println("Selected other"); } } }
代码解释:
- 由于
case 1
和case 2
中都缺少break
语句,当option = 2
时,程序会依次输出"Selected 2"
、"Selected 3"
和"Selected other"
。
- 由于
多个 Case 共享代码块:多个
case
可以共享同一组语句块。// 多个 case 共享代码块的示例 public class Main { public static void main(String[] args) { int option = 2; switch (option) { case 1: System.out.println("Selected 1"); break; case 2: case 3: System.out.println("Selected 2, 3"); break; default: System.out.println("Selected other"); break; } } }
代码解释:
- 当
option
的值为 2 或 3 时,都会执行System.out.println("Selected 2, 3");
。
- 当
Case 的顺序:只要保证有
break
,case
的顺序不影响程序逻辑,但建议按照自然顺序排列,便于阅读。匹配字符串:
switch
语句可以匹配字符串,字符串匹配时比较的是 " 内容相等 "。// switch 语句匹配字符串的示例 public class Main { public static void main(String[] args) { String fruit = "apple"; switch (fruit) { case "apple": System.out.println("Selected apple"); break; case "pear": System.out.println("Selected pear"); break; case "mango": System.out.println("Selected mango"); break; default: System.out.println("No fruit selected"); break; } } }
4.3.3 Switch 表达式 (Java 12+)
Java 12 引入了更简洁的 switch
表达式语法,它保证只有一种路径会被执行,并且不需要 break
语句,从而避免了 case
穿透问题。
// switch 表达式示例
public class Main {
public static void main(String[] args) {
String fruit = "apple";
switch (fruit) {
case "apple" -> System.out.println("Selected apple");
case "pear" -> System.out.println("Selected pear");
case "mango" -> {
System.out.println("Selected mango");
System.out.println("Good choice!");
}
default -> System.out.println("No fruit selected");
}
}
}
代码解释:
- 新语法使用
->
,如果有多条语句,需要用{}
括起来。 - 不需要写
break
语句,因为新语法只会执行匹配的语句,没有穿透效应。
switch
表达式可以直接返回值,使代码更简洁。
// switch 表达式返回值的示例
public class Main {
public static void main(String[] args) {
String fruit = "apple";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> 0;
}; // 注意赋值语句要以;结束
System.out.println("opt = " + opt);
}
}
4.3.4 Yield 关键字
如果 switch
表达式内部需要执行复杂的语句,可以使用 {...}
括起来,并使用 yield
返回一个值作为 switch
语句的返回值。
// 使用 yield 的 switch 表达式示例
public class Main {
public static void main(String[] args) {
String fruit = "orange";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> {
int code = fruit.hashCode();
yield code; // switch 语句返回值
}
};
System.out.println("opt = " + opt);
}
}
代码解释:
- 当
fruit
不匹配任何case
时,default
分支的代码块会被执行,计算fruit
的hashCode()
,并通过yield
关键字将其作为switch
表达式的返回值。
总而言之,switch
语句提供了一种清晰高效的多路选择机制。而 Java 12 引入的 switch
表达式,通过更简洁的语法和更强的安全性,进一步提升了代码的可读性和可维护性。在实际开发中,可以根据具体情况选择合适的 switch
结构。
4.4 while
循环
循环语句用于指示计算机根据条件重复执行计算。当条件满足时,循环继续;当条件不满足时,循环退出。
Java 中的 while
循环基本语法如下:
while (条件表达式) {
循环语句
}
// 继续执行后续代码
在每次循环开始之前,while
循环会首先判断条件表达式的结果。如果结果为 true
,则执行循环体内的语句;如果结果为 false
,则跳过循环体,继续执行 while
循环之后的代码。
以下代码展示了如何使用 while
循环计算 1 到 100 的累加和:
// while
public class Main {
public static void main(String[] args) {
int sum = 0; // 累加的和,初始化为0
int n = 1;
while (n <= 100) { // 循环条件是n <= 100
sum = sum + n; // 把n累加到sum中
n ++; // n自身加1
}
System.out.println(sum); // 5050
}
}
在这个例子中,变量 sum
用于存储累加的和,初始值为 0。变量 n
从 1 开始,每次循环都将 n
加到 sum
中,并将 n
的值增加 1。循环会一直执行,直到 n
的值大于 100 为止。
需要注意的是,while
循环是先判断循环条件,再执行循环体,因此,循环体有可能一次都不会执行。
在编写循环时,需要特别注意循环条件和自增变量的边界条件。如果边界条件处理不当,可能会导致循环无法得到正确的结果。例如:
// while
public class Main {
public static void main(String[] args) {
int sum = 0;
int n = 0;
while (n <= 100) {
n ++;
sum = sum + n;
}
System.out.println(sum);
}
}
上述代码中,由于 n
的初始值为 0,并且在循环体内部先执行 n ++
,再执行 sum = sum + n
,导致最终的累加结果包含了 101,因此结果不正确。
如果循环条件永远满足,那么循环将变成死循环,导致 CPU 占用率过高,程序无法正常运行。因此,在编写循环时,要确保循环条件最终能够变为 false
,从而使循环能够正常退出。
如果循环条件的逻辑写得有问题,也可能造成意料之外的结果,例如:
// while
public class Main {
public static void main(String[] args) {
int sum = 0;
int n = 1;
while (n > 0) {
sum = sum + n;
n ++;
}
System.out.println(n); // -2147483648
System.out.println(sum);
}
}
这段代码表面上是一个死循环,但实际上,由于 int
类型有最大值限制,当 n
达到最大值后,再加 1 会导致溢出,变为负数,从而使循环条件 n > 0
不再满足,循环意外退出。
4.5 do while
循环
在 Java 中,do while
循环与 while
循环的区别在于,do while
循环会先执行循环体,然后再判断循环条件。这意味着 do while
循环至少会执行一次。当条件满足时,循环继续执行;当条件不满足时,循环退出。
其基本语法格式如下:
do {
// 执行循环语句
} while (条件表达式);
以下代码展示了如何使用 do while
循环来计算 1 到 100 的和:
// do-while
public class Main {
public static void main(String[] args) {
int sum = 0;
int n = 1;
do {
sum = sum + n;
n ++;
} while (n <= 100);
System.out.println(sum);
}
}
代码解释:
- 初始化变量: 首先,我们初始化了两个变量
sum
和n
。sum
用于存储累加的和,初始值为 0。n
是计数器,初始值为 1。 do
循环体:do
循环体中的代码会被至少执行一次。sum = sum + n;
:将当前的n
值加到sum
中,实现累加。n++;
:将n
的值加 1,为下一次循环做准备。
while
条件判断:while (n <= 100);
这行代码判断循环是否继续执行。只有当n
小于等于 100 时,循环才会继续。- 输出结果: 当
n
大于 100 时,循环结束,程序输出sum
的值,即 1 到 100 的和。
使用 do while
循环时,同样需要注意循环条件的判断,避免出现无限循环。
4.6 for
循环
for
循环是 Java 中使用最广泛的循环结构之一,它通过计数器实现循环功能。for
循环在执行前会初始化计数器,每次循环前检查循环条件,并在每次循环后更新计数器。计数器变量通常被命名为 i
。
4.6.1 for
循环的基本结构
for
循环的基本语法结构如下:
for (初始条件; 循环检测条件; 循环后更新计数器) {
// 执行语句
}
- 初始条件 (initialization): 定义并初始化计数器变量,例如
int i = 1
。这个部分只在循环开始前执行一次。 - 循环检测条件 (condition): 每次循环开始前都会检查这个条件。如果条件为
true
,则执行循环体;如果为false
,则循环结束。例如i <= 100
。 - 循环后更新计数器 (increment/decrement): 每次循环体执行完毕后,执行这个语句来更新计数器变量。例如
i++
。
以下代码使用 for
循环计算 1 到 100 的和:
public class Main {
public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum = sum + i;
}
System.out.println(sum);
}
}
在这个例子中,计数器变量 i
从 1 开始,每次循环 i
的值增加 1,直到 i
的值大于 100 时,循环结束。
以下代码使用 for
循环遍历一个整型数组,并计算数组元素的总和:
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
int sum = 0;
for (int i = 0; i < ns.length; i++) {
System.out.println("i = " + i + ", ns[i] = " + ns[i]);
sum = sum + ns[i];
}
System.out.println("sum = " + sum);
}
}
ns.length
表示数组ns
的长度。- 循环条件
i < ns.length
确保循环不会超出数组的索引范围。
注意事项
- 避免在循环体内修改计数器:在循环体内部修改计数器变量可能会导致逻辑错误。应该将计数器的初始化、判断条件和更新统一放在
for()
语句中。 - 计数器变量的作用域:尽量将计数器变量
i
定义在for
循环内部,以缩小变量的访问范围。如果在循环外部定义计数器变量,退出循环后仍然可以访问该变量,这可能会破坏变量的作用域最小化原则。
4.6.2 灵活使用 for
循环
for
循环的初始化语句、循环条件和每次循环更新语句都可以省略,例如:
// 不设置结束条件:
for (int i = 0; ; i++) {
...
}
// 不设置结束条件和更新语句:
for (int i = 0; ;) {
...
}
// 什么都不设置:
for (;;) {
...
}
虽然在某些情况下可以省略 for
循环的某些语句,但通常不推荐这样做,因为它可能会降低代码的可读性。
4.6.3 for each
循环
for each
循环,也称为增强型 for
循环,提供了一种更简洁的方式来遍历数组和集合。
示例:使用 for each
循环遍历数组
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}
}
}
在这个例子中,变量 n
直接对应到数组 ns
的每个元素,而不需要使用索引。
for each
循环的优点和缺点:
- 优点:语法简洁,易于理解。
- 缺点:无法指定遍历顺序,也无法获取数组的索引。
除了数组外,for each
循环可以遍历所有“可迭代”的数据类型,包括 List
、Map
等集合。
总而言之,for
循环和 for each
循环各有优缺点,选择哪种循环取决于具体的应用场景。如果需要使用索引或者需要控制遍历顺序,则应使用 for
循环;如果只需要简单地遍历数组或集合中的元素,则可以使用 for each
循环。
好的,下面是对您提供的 Java 课件关于 break
和 continue
语句的整理笔记。
4.7 break
和 continue
语句
在 while
循环和 for
循环中,break
和 continue
语句可以用来控制循环的执行流程。
4.7.1 break
语句
break
语句用于跳出当前循环体,不再执行循环中 break
语句后面的任何代码,直接退出循环。
以下代码展示了如何使用 break
语句在 for
循环中计算 1 到 100 的和:
// break
public class Main {
public static void main(String[] args) {
int sum = 0;
for (int i=1; ; i++) {
sum = sum + i;
if (i == 100) {
break;
}
}
System.out.println(sum);
}
}
在这个例子中,for
循环没有设置退出条件,而是通过 if
语句判断 i
是否等于 100,如果等于 100,则执行 break
语句跳出循环。
break
语句总是跳出它所在的那一层循环。在嵌套循环中,break
语句只会跳出当前所在的内层循环,而不会影响外层循环的执行。
// break
public class Main {
public static void main(String[] args) {
for (int i=1; i<=10; i++) {
System.out.println("i = " + i);
for (int j=1; j<=10; j++) {
System.out.println("j = " + j);
if (j >= i) {
break;
}
}
// break跳到这里
System.out.println("breaked");
}
}
}
在上述代码中,当内层循环的 j
大于等于外层循环的 i
时,break
语句会跳出内层循环,程序会继续执行外层循环的下一轮迭代。
4.7.2 continue
语句
continue
语句用于提前结束本次循环,直接开始下一次循环的迭代。循环体中 continue
语句后面的代码不会被执行。
以下代码展示了 continue
语句的用法,计算 1 到 10 之间所有奇数的和:
// continue
public class Main {
public static void main(String[] args) {
int sum = 0;
for (int i=1; i<=10; i++) {
System.out.println("begin i = " + i);
if (i % 2 == 0) {
continue; // continue语句会结束本次循环
}
sum = sum + i;
System.out.println("end i = " + i);
}
System.out.println(sum); // 25
}
}
在这个例子中,当 i
是偶数时,continue
语句会结束本次循环,直接进入下一次循环,因此 sum = sum + i;
和 System.out.println("end i = " + i);
这两行代码不会被执行。当 i
是奇数时,则会完整执行整个循环体。
与 break
类似,在多层嵌套的循环中,continue
语句也仅仅会结束当前所在循环的本次迭代。