5.1 顺序与选择结构
5.1.1 顺序结构
顺序结构是程序中最简单、最基本的流程控制结构,它按照程序中语句出现的先后顺序依次执行,直到程序的结束。
顺序结构示例:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
//上述程序中,只有一条语句`System.out.println("Hello, World!");`,它按照顺序执行,输出"Hello, World!"。
5.1.2 选择结构
选择结构又叫分支结构,是根据条件表达式的值来决定程序的执行路径。Java语言提供了两种选择结构:if语句和switch语句。
5.1.2.1 if语句
if语句用于根据条件的真假来决定程序的执行路径。if语句有三种形式:if语句、if-else语句和if-else if-else语句。
if语句:if语句用于根据条件表达式的值来决定是否执行某段代码。如果条件表达式的值为true,则执行if语句块中的代码;如果条件表达式的值为false,则不执行if语句块中的代码。
1.单分支结构
if (条件表达式) {
// 条件为真时执行的代码块
}
示例:
int num = 10;
if (num > 0) {
System.out.println("num是正数");
}
System.out.println("程序结束");
2.双分支结构
if (条件表达式) {
// 条件为真时执行的代码块
} else {
// 条件为假时执行的代码块
}
示例:
int num = 10;
if (num > 0) {
System.out.println("num是正数");
}
else {
System.out.println("num是负数");
}
System.out.println("程序结束");
3.多分支结构
if (条件表达式1) {
// 条件1为真时执行的代码块
} else if (条件表达式2) {
// 条件2为真时执行的代码块
} else if (条件表达式3) {
// 条件3为真时执行的代码块
} else {
// 所有条件都为假时执行的代码块
}
示例:
int num = 10;
if (num > 0) {
System.out.println("num是正数");
}
else if (num < 0) {
System.out.println("num是负数");
}
else {
System.out.println("num是零");
}
System.out.println("程序结束");
多分支结构典型应用,求一元二次方程的根。中国古代数学家秦九韶提出了“秦九韶算法”,该算法可以用来求解一元二次方程。一元二次方程的一般形式为:ax2+bx+c=0,其中a、b、c为常数,且a≠0。根据判别式b2-4ac的值,可以判断方程的根的情况:
import java.util.Scanner;
import java.lang.Math;
Scanner input = new Scanner(System.in);
System.out.print("请输入a的值:");
double a = input.nextDouble();
System.out.print("请输入b的值:");
double b = input.nextDouble();
System.out.print("请输入c的值:");
double c = input.nextDouble();
double delta = b * b - 4 * a * c;
if (delta > 0.0) {
double x1 = (-b + Math.sqrt(delta)) / (2 * a);
double x2 = (-b - Math.sqrt(delta)) / (2 * a);
System.out.println("方程有两个实根:x1=" + x1 + ",x2=" + x2);
} else if (delta == 0.0) {
double x = -b / (2 * a);
System.out.println("方程有一个实根:x=" + x);
} else {
double realPart = -b / (2 * a); // 实部
double q=Math.sqrt(Math.abs(delta) / (2 * a) ;//虚部
System.out.println("方程有两个虚根:x1=" + realPart + "+" + q + "i,x2=" + realPart + "-" + q + "i");
}
if(1/(10^6)==0.0){
System.out.println("1/(10^6)等于0");
}
//1/(10^6)等于0
5.1.2.2 嵌套if语句
格式:
if(条件1){
if(条件2){
//当条件1和条件2同时为真时执行
}else{
//当条件1为真,条件2为假时执行
}
}else{
//当条件1为假时执行
}
示例:
例如:
```Java
int a=2;
int b=-3;
if(a>0){
if(b>0){
System.out.println("a和b都是正数");
}else{
System.out.println("a是正数,b是负数");
}
}else{
System.out.println("a是负数");
}
```Java
if……else语句的嵌套可以有很多层,但要注意嵌套的层次不宜过多,否则会使程序的结构变得复杂,难以阅读和维护。
***
5.1.3 switch语句
1.switch语句的基本格式
switch语句用于根据表达式的值选择多个代码块中的一个执行,其格式如下:
switch(表达式){
case 常量1:
//当表达式的值等于常量1时执行的代码
break;
case 常量2:
//当表达式的值等于常量2时执行的代码
break;
...
default: //当表达式的值与所有case后面的常量都不相等时执行的代码
}
示例:
import java.util.Scanner;
Scanner input = new Scanner(System.in);
System.out.println("请输入a的值:");
int a = input.nextInt();
switch(a){
case 1:
System.out.println("a的值为1");
break;
case 2:
System.out.println("a的值为2");
break;
case 3:
System.out.println("a的值为3");
break;
// default:
// System.out.println("a的值不是1、2、3");
}
- 1.程序执行过程:
- 首先创建一个Scanner对象,用于从控制台读取用户输入的整数。
- 然后使用switch语句根据用户输入的整数a的值执行不同的操作。
- 如果a的值为1,则输出"a的值为1"。
- 如果a的值为2,则输出"a的值为2"。
- 如果a的值为3,则输出"a的值为3"。
- 如果a的值不是1、2、3,则输出"a的值不是1、2、3"。
- 最后关闭Scanner对象,释放资源。
- 2.程序输出结果:
- 如果用户输入的整数a的值为1,则输出"a的值为1"。
- 如果用户输入的整数a的值为2,则输出"a的值为2"。
- 如果用户输入的整数a的值为3,则输出"a的值为3"。
- 如果用户输入的整数a的值不是1、2、3,则输出"a的值不是1、2、3"。
1.switch语句执行的过程:
- 计算表达式的值,然后与每个case后面的常量进行比较。
- 如果表达式的值与某个case后面的常量相等,则执行该case后面的代码块。
- 如果表达式的值与所有case后面的常量都不相等,则执行default后面的代码块(如果有的话)。
- 如果某个case后面的代码块中包含了break语句,则执行完该代码块后,switch语句结束。
2.switch语句的特点:
- switch语句中的表达式可以是byte、short、int、char、String类型,case后面的常量值必须与表达式的类型相同。
- switch语句中的case后面的常量值必须是唯一的,不能重复。
- switch语句中的break语句用于终止switch语句的执行,如果不加break语句,程序会继续执行下一个case语句,直到遇到break语句或switch语句结束为止。4.
- switch语句中的case语句和default语句的顺序可以任意,但一般建议将default语句放在最后,以避免遗漏。
- default语句是可选的,用于处理所有case语句都不匹配的情况。
- switch语句中的case语句和default语句后面的代码块可以包含任意合法的Java语句,包括变量声明、赋值语句、控制语句等。
- switch语句实现的多分支结构,完全可以用if-else语句实现,但switch语句在某些情况下可以使代码更简洁、更易读。
switch语句典型应用:
import java.util.Scanner;
Scanner scan=new Scanner(System.in);
System.out.println("请输入分数=");
int score=scan.nextInt();
switch(score/10){
case 10:
case 9:
System.out.println("优秀");
break;
case 8:
System.out.println("良好");
break;
case 7:
System.out.println("中等");
break;
case 6:
System.out.println("及格");
break;
default:
System.out.println("不及格");
break;
}
2.switch语句的嵌套
import java.util.Scanner;
Scanner scan=new Scanner(System.in);
System.out.print("请输入成绩=");
int score=scan.nextInt();
System.out.printf("%d\n成绩等级是:",score);
switch(score/10){
case 10:
case 9:
System.out.println("优秀");
switch(score){
case 90:System.out.println("奖励一台手机");break;
case 91:case 92:case 93:case 94:case 95:case 96:case 97:case 98:case 99:case 100: System.out.println("奖励一台电脑");break;
}
break;
case 8:
System.out.println("良好");
break;
case 7:
System.out.println("中等");
break;
case 6:
System.out.println("及格");
break;
default:
System.out.println("不及格");
break;
}
3.switch新特性
(1)Java12版本switch语句新特性
Switch 表达式在java12中引入了 -> 操作符,用于替代传统的冒号(:)。
与传统的 Switch 语句不同,使用 -> 的 case 分支不会出现 "fall-through" 现象,因此不需要 break 语句来防止穿透。这减少了代码的复杂性,也降低了编程错误的风险。
//利用java12的新我,上面的例子可以这样写
switch(score){
case 90,91,92,93,94,95,96,97,98,99,100->System.out.println("奖励一台电脑");
case 80,81,82,83,84,85,86,87,88,89->System.out.println("奖励一台手机");
default->System.out.println("奖励一张奖状");
}
//老版写法
public static String getTypeOfDay(String day) {
String typeOfDay;
switch (day) {
case "MONDAY":
case "TUESDAY":
case "WEDNESDAY":
case "THURSDAY":
case "FRIDAY":
typeOfDay = "Weekday";//工作日
break;
case "SATURDAY":
case "SUNDAY":
typeOfDay = "Weekend";//周末
break;
default:
typeOfDay = "Unknown"; //未知
break;
}
return typeOfDay;
}
import java.util.Scanner;
import java.lang.String;
public class MySwitch {
//新版写法
public static String getTypeOfDay(String day) {
return switch (day) {
case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> "Weekday";
case "SATURDAY", "SUNDAY" -> "Weekend";
default -> "Unknown";
};
}
}
MySwitch.getTypeOfDay("MONDAY"); // "Weekday"
MySwitch.getTypeOfDay("FRIDAY"); // "Weekday"
MySwitch.getTypeOfDay("SATURDAY"); // "Weekend"
是不是简洁了很多。从这里可以看出他们之间的差异如下:
- 返回1值:老的 Switch 仅仅只是语句,用来控制流程的,无法返回值。但是新的 Switch 可以作为表达式使用,支持返回值。
1
->** 代替**:老的 Switch 需要在每个案例后面使用 break ,否则会发生“穿透”,而新的不需要,它会自动终止。- 多值匹配:老的 Switch 无法在一个案例标签中匹配多个值,而新的 Switch 表达式允许一个 case 匹配多个值,用"," 分割即可。
- 更简洁的语法: 整体代码更简洁,易于阅读和维护。
ava 12 引入 Switch 表达式,它解决了传统 Switch 语句的两个缺陷:
- "Fall-through" 行为:在没有显式 break 语句的情况下,Switch 语句会从一个 case "穿透" 到下一个 case,忽略了这个会导致不可饶恕的错误。
- 代码冗余:每个 case,我们都需要重复类似的代码结构,增加了代码的冗余和维护难度。
(2)Java13版本switch语句新特性
Java13对Switch表达式引入yield
关键字来处理多分支结构中的返回值
Java12虽然解决这两个问题,但是还是有一个不好的地方,就是返回值,比如在处理复杂的逻辑时,仍需依赖外部变量来返回结果。所以Java13对Switch表达式进行了扩展,引入 yield关键字来处理多分支结构中的返回值,如下:
public static String getTypeOfDay(String day) {
return switch (day) {
case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> "Weekday";
case "SATURDAY", "SUNDAY" -> "Weekend";
default -> {
if (day.isEmpty()) {
"day is empty"; //yield
} else {
"Unknown"; //yield
}
}
};
}
getTypeOfDay("MONDAY"); // "Weekday"
getTypeOfDay("SATURDAY"); // "Weekend"
getTypeOfDay(""); // "day is empty"
getTypeOfDay("FREDAY"); // "Unknown"
(3)Java17版本switch语句新特性
Java 17 模式匹配的应用扩展到了switch表达式
在Java 17中,switch表达式可以应用模式匹配,以提供更简洁和直观的语法。这使得switch表达式在处理复杂逻辑时更加灵活和强大。
switch 模式匹配支持以下几种模式:
- 类型模式
- 空模式
- 守卫模式
- 常量模式
类型模式
不知道同学们注意没有,Switch 表达式只有一种类型,如果我们要处理多种类型呢?我们只能这样处理
//举个例子来说明下:
public void switchTest() {
Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" };
for (Object obj: objects) {
if (obj instanceof Integer intR) {
System.out.println("为整数型:" + intR);
} else if (obj instanceof Float floatR) {
System.out.println("为浮点型:" + floatR);
} else if (obj instanceof Double doubleR) {
System.out.println("为双精度浮点数:" + doubleR);
} else if (obj instanceof String str) {
System.out.println("为字符串:" + str);
} else {
System.out.println("其他类型:" + obj);
}
}
}
//我们用 Switch 表达式来改造下:
@Test
public void switchTest() {
Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" };
for (Object obj: objects) {
switch (obj) {
case Integer intR -> System.out.println("为整数型:" + intR);
case Float floatR -> System.out.println("为浮点型:" + floatR);
case Double doubleR -> System.out.println("为双精度浮点数:" + doubleR);
case String str -> System.out.println("为字符串:" + str);
default -> System.out.println("其他类型:" + obj);
}
}
}
相比上面的 if...else 简洁了很多。同时在 Java 17 之前,Switch 选择器表达式只支持特定类型,即基本整型数据类型byte、short、char和int;对应的装箱形式Byte、Short、Character和Integer;String类;枚举类型。现在有了类型模式,Switch 表达式可以是任何类型啦。
空模式
在Java17之前,向switch语句传递一个null值,会抛出一个NullPointerException,现在可以通过类型模式,将 null 检查作为一个单独的case标签来处理,如下:
public void switchTest() {
Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" };
for (Object obj: objects) {
switch (obj) {
// 省略...
case null -> System.out.println("为空值");
default -> System.out.println("其他类型:" + obj);
}
}
}
//case null 可以直接匹配值为 null 的情况。
守卫模式
守卫模式允许我们在 case 标签后添加一个额外的条件。只有当类型匹配并且额外条件为真时,才会进入该 case 块。
比如上面例子,我们要将字符串那块逻辑调整下,比如长度大于 5 的为长字符串,小于 5 的为短字符串,在不使用守卫模式的情况下,我们一般这样写:
public void switchTest() {
Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" };
for (Object obj: objects) {
switch (obj) {
case Integer intR -> System.out.println("为整数型:" + intR);
case Float floatR -> System.out.println("为浮点型:" + floatR);
case Double doubleR -> System.out.println("为双精度浮点数:" + doubleR);
case String str -> {
if (str.length() > 5) {
System.out.println("为长字符串:" + str);
} else {
System.out.println("为短字符串:" + str);
}
}
case null -> System.out.println("为空值");
default -> System.out.println("其他类型:" + obj);
}
}
}
这种写法就显得不是那么友好,使用守卫模式如下:
public void switchTest() {
Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" };
for (Object obj: objects) {
switch (obj) {
case Integer intR -> System.out.println("为整数型:" + intR);
case Float floatR -> System.out.println("为浮点型:" + floatR);
case Double doubleR -> System.out.println("为双精度浮点数:" + doubleR);
case String str && str.length() > 5 -> System.out.println("为长字符串:" + str);
case String str -> System.out.println("为短字符串:" + str);
case null -> System.out.println("为空值");
default -> System.out.println("其他类型:" + obj);
}
}
}
//复制代码
//使用守卫模式,我们可以编写更灵活和表达性强的代码。