- 与任何程序设计语言一样,Java 支持使用 条件语句 和 循环结构 来确定 控制流程。
01、代码块(block)
- 块(即:复合语句)由若干条 Java 语句组成,并用一对大括号括起来。
- 块确定了变量的作用域。
- 一个块可以嵌套在另一个块中。
public static void main(String[] args){
int n;
// ……
// 嵌套代码块
{
int k;
} // k is only defined up to here
}
- 不能在嵌套的两个块中声明同名的变量。
- 如:下面的代码就有错误,而无法通过编译:
public static void main(String[] args){
int n;
// ……
{
// k 的【作用域】仅在 内部 的代码块内。
int k;
int n; // 此时,会编译报错:不能在嵌套的两个块中,声明同名的变量。
}
}
02、顺序结构
- 代码中没有流程控制,程序是自上而下执行的,一条语句执行完成,执行下一行语句,直到程序的最后。
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// false
System.out.println(Float.NaN == Double.NaN);
// true
System.out.println(Float.NEGATIVE_INFINITY == Double.NEGATIVE_INFINITY);
// true
System.out.println(Float.POSITIVE_INFINITY == Double.POSITIVE_INFINITY);
}
03、分支结构 之 if 语句
- 基本语法:
- if (表达式){ 一条或多条语句 } else if (表达式){ 一条或多条语句 } else { 一条或多条语句 }
- 注意事项:
- if () 或 else 后边没有 {},其控制范围 仅限于第一句。
public static void main(String[] args) {
if(args.length == 0) {
System.out.println("没有参数");
} else if (args [0] .equals ("-h")) {
System.out.print("Hello,");
} else if (args [0] .equals ("-g")) {
System.out.print("Goodbye,");
} else
// else 只能执行这一行。
System.out.println(Arrays.toString(args));
System.out.println("执行完毕!!!");
}
04、分支结构 之 switch 语句
- 在处理同一个表达式的 多个选项 时,用 if/else 结构会显得有些笨拙。
- switch 语句会让这个工作变得容易。
- switch 能解决的,if 都能解决,if 能解决的 switch 不一定能解决。
1、java 8 之前的 switch 语句
- switch 后边的 () 中的内容是一个常量或常量表达式。
- jdk1.0 首次引入 switch 语句,支持 byte、char、short、int 的常量或常量表达式。
- jdk1.5 加入了 枚举常量。
- jdk1.7 加入了 String 常量。
- switch 中的 case 语句是一个条件选择语句,找到相同的 case 值作为入口,则执行后边的语句;
- 若 所有的 case 值都不满足,则找 default 入口;
- 若 找不到 default 入口,则退出整个 switch 语句。
- 所以,default 只是一个备用的入口。可有可无。
- 当 default 语句在所有的 case 语句 之前,并且 default 后边的语句不是以 break 结束的。
* 当所有 case 都不匹配时,它会执行 default 语句之后的 case 语句,直到遇到 break 才停止。
public class SwitchTest {
public static void main(String[] args) {
int a = 0;
switch(a){
default:System.out.println("aaa");
break;
case 0 :System.out.println("00000");
break;
case 1:
case 2:
case 3:System.out.println("11111");
break;
}
}
}
当a为0时:执行结果为:00000
当a为0时:把所有的break;都删除掉。执行结果为:00000 11111
当a为1时:执行结果为:11111
当a为2时:执行结果为:11111
当a为3时:执行结果为:11111
当a为4时:执行结果为:aaa
当a为4时:把所有的break;都删除掉。执行结果为:aaa 00000 11111
2、java 14 的 switch 语句
- 1、支持箭头语法(->),可避免穿透:
public static void main(String[] args) {
int x = 4;
switch (x) {
// 无需使用 break,也不用担心下边的 case 语句被执行了。
default -> System.out.println("其它");
case 1 -> System.out.println("1");
case 2 -> System.out.println("2");
}
}
- 2、可以为各个 case 提供多个标签,用逗号分隔。
- 3、支持 switch 表达式返回值。
public static void main(String[] args) {
int day = 8;
String dayType = switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> throw new IllegalArgumentException();
};
System.out.println(dayType);
}
- 4、当需要在 switch 表达式中执行多行代码时,可以使用 yield 关键字返回值:
public static void main(String[] args) {
String day = "MONDAY";
String result = switch (day) {
case "MONDAY" -> {
System.out.println("这是一周的开始");
yield "星期一";
}
case "TUESDAY" -> {
System.out.println("继续努力");
yield "星期二";
}
default -> "其他日子";
};
}
3、java 17 的 switch 语句
- 类型模式:case Type variable -> … 直接匹配 对象类型,并 绑定变量。
- 自动类型转换:无需手动 instanceof 和强制转换。
public static void main(String[] args) {
Object obj = -1;
// 与 instanceof 模式类似,每个类型模式会【声明一个变量】.
String result = switch (obj) {
// 在 case 中通过 when 添加额外条件,实现更细粒度的匹配。
case Integer i when i > 0 -> "正数: " + i;
case Integer i -> "非正数: " + i;
case String s -> "字符串: " + s;
case Double d -> "浮点数: " + d;
// 允许在 switch 中显式处理 null,避免 NullPointerException。
case null -> "这是null值";
default -> "未知类型";
};
System.out.println(result); // 输出: 字符串: Hello
}
- 穷举性检查:若 switch 覆盖所有可能的类型(如:结合 密封类),无需 default。
// 定义密封类
public sealed abstract class Shape permits Circle, Rectangle, Triangle {
public abstract double area();
}
public final class Circle extends Shape {
private final double radius;
public Circle(double r) { radius = r; }
@Override
public double area() { return Math.PI * radius * radius; }
}
public final class Rectangle extends Shape {
private final double width, height;
public Rectangle(double w, double h) { width = w; height = h; }
@Override
public double area() { return width * height; }
}
public final class Triangle extends Shape { /* 类似实现 */ }
// 穷举性 switch
Shape shape = new Circle(5.0);
double area = switch (shape) {
case Circle c -> c.area();
case Rectangle r -> r.area();
case Triangle t -> t.area();
// 无需 default,因为 Shape 是密封类,已覆盖所有子类
};
4、java 21 的 switch 语句
- 解构记录类(Record):
public class SwitchTest {
// 解构记录类(Record):结合记录类的模式匹配,直接解构字段值。
record Color(int r, int g, int b) {}
public static void main(String[] args) {
Object obj = new Color(255, 0, 0);
// Object obj = "不停的脚步";
String desc = switch (obj) {
case Color(int r, int g, int b) -> String.format("RGB: (%d, %d, %d)%n", r, g, b);
case String s -> "字符串: " + s;
default -> "其他类型";
};
System.out.println(desc); // 输出: RGB: (255, 0, 0)
}
}
05、switch 语句 vs if 语句
1、适用场景 对比
场景 | switch 的优势 | if-else 的优势 |
---|---|---|
多分支等值判断 | 当需要匹配固定常量值(如枚举、字符串、整数)时更简洁 | 不适用,代码会冗长 |
范围或复杂逻辑判断 | 不支持范围判断(如:x > 10)或逻辑组合(如:&&) | 可直接处理复杂逻辑。 如:if (x > 10 && y < 5) |
动态条件 | 仅支持常量值,无法在运行时动态生成条件 | 支持动态生成的布尔表达式 |
- 如果 判断条件为 区间,则最好使用多重 if 来做,
- 如果是 多个 等值 情况 最好使用 switch 来做。
2、性能 对比
维度 | switch | if-else |
---|---|---|
底层实现 | 可能被编译为跳转表 (Jump Table),时间复杂度接近 O(1) | 逐个条件判断,时间复杂度为 O(n)(n 为分支数) |
优化场景 | 当case值连续且密集时,性能更优。如:case 1,2,3 | 分支较少时效率高,分支过多时性能下降 |
实际差异 | 通常更快(尤其是分支较多时) | 在分支较少时差异可忽略 |
3、switch 的性能优化:
- tableswitch:适用于连续 case 值,通过跳转表实现 O(1) 时间复杂度。
- 当使用 tableswitch 时,JVM 会生成一个 连续的 内存地址表,直接通过输入值的偏移量跳转到目标代码块。
- 优势:执行效率高,无需比较操作。
- 内存开销:需要为最小到最大的 case 值分配连续空间,即使某些位置未使用。
- lookupswitch:适用于稀疏 case 值,通过二分查找实现 O(log n) 时间复杂度。
- 字符串 switch:因哈希计算和二次验证,性能稍弱于整数 switch。
- 但仍优于 if-else 链。
- 枚举 switch:转换为 ordinal() 值的 switch,高效且安全。
- 编译为对 day.ordinal() 的 switch(如 0, 1, 2)时,
- 使用 tableswitch 或 lookupswitch。
06、循环结构 之 for 循环
- 语法:for(初始表达式; 布尔表达式; 迭代因子){ 循环体; }
- for 循环是一种支持 迭代 的一种通用结构。
- 它由一个 计数器 或 类似的变量控制迭代次数,每次迭代后这个变量 将会更新。
for(初始化表达式(1);循环条件表达式(2);循环后的操作表达式(3)){
执行语句;(4)
…;
}
执行顺序:(1) → (2) → (4) → (3) → (2) → (4) → (3)
- 逗号操作符:
/**
* 逗号操作符
*/
public static void commaOperator(){
for (int i = 1, j = i + 10; i < 5; i++, j = i * 2){
System.out.println("i = " + i + ",\tj = " + j);
}
}
/*
输出结果:
i = 1, j = 11
i = 2, j = 4
i = 3, j = 6
i = 4, j = 8
*/
- 以下 Java 程序的输出是什么?
- 如下代码将导致编译错误,因为 for 循环的第二部分是非布尔表达式,并且给定是一个整数值,即 0。
class Test {
public static void main (String args[]) {
for(int i=0; 0; i++) {
System.out.println("Hello Javatpoint");
}
}
}
07、循环结构 之 while 循环
- 语法:while(布尔表达式){ 循环体; }
- 先判断逻辑表达式,再执行语句;
- 如果开始时循环条件就为 false 。那么,while 循环 一次也不执行。
Scanner sc = new Scanner(System.in);
System.out.println("准备执行 hasNextLine");
//判断是否还有下一行
while (sc.hasNextLine()) {
System.out.println("sc.nextLine:" + sc.nextLine());
}
System.out.println("跳过 hasNextLine");
- 注意:
- 在循环中,检测 两个浮点数是否相等 需要格外小心。
int count = 0;
for (double x = 0; x != 1; x += 0.1) {
System.out.println(++count + "\t" + x);
}
/**
输出前 13 次结果:
1 0.0
2 0.1
3 0.2
4 0.30000000000000004
5 0.4
6 0.5
7 0.6
8 0.7
9 0.7999999999999999
10 0.8999999999999999
11 0.9999999999999999
12 1.0999999999999999
13 1.2
*/
- 循环可能永远不会结束,由于存在舍入误差,可能 永远达不到 精确的最终值。
- 由于 0.1 无法精确地用二进制表示。
- 所以,x 将从 0.9999999999999999 跳到 1.0999999999999999 。
08、循环结构 之 do{ }while() 循环
- 语法:do{ 循环体; } while (布尔表达式);
- 先执行语句,在判断逻辑表达式;循环体 至少执行一次。
09、循环结构 之 foreach 循环
- 语法:for( 数组或集合中元素的类型 迭代变量 : 数组或集合名 ){ 循环体; }
- 可以用来**依次处理(数组 或 集合) 中的每个元素, 而不必考虑指定索引值**。
10、简单的无限循环格式
- 无限循环存在的原因是并不知道循环多少次,是根据某些条件,来控制循环。
- while(true){ 循环体; }
- for(;😉 { 循环体; }
11、中断控制流程的语句
1、语句类型
- break:终止该层循环;
- continue:跳过该层循环(忽略continue下边的语句)
- return:结束整个方法;整个方法的执行都会结束。
2、break outer; 与 continue outer;
- ①:outer 标签 的出现,可以让这两个语句作用于指定的循环。
- ②:若这两个语句离开应用范围,存在是没有意义的。
- ③:这个两个语句后面都不能有语句,因为执行不到。
- ④:continue语句是跳过本次循环,继续下次循环。
- break outer; 语句
// Break、continue后可以紧跟标号:
// 带标号的 break 循环,用于结束标号所在的循环。
public class Example1 {
public static void main(String[] args) {
outer:
for (int i=0 ; i<10 ; i++) {
for (int j = 0; j < 10; j++) {
System.out.println("i的值是"+i+",j的值是"+j);
if(j>3){
break outer;
}
}
}
}
}
运行结果是:
i的值是0,j的值是0
i的值是0,j的值是1
i的值是0,j的值是2
i的值是0,j的值是3
i的值是0,j的值是4
- continue outer; 语句
// 带标号的 continue 用于忽略标号所标识的循环后面剩下的语句。
public class Example1 {
public static void main(String[] args) {
outer:
for (int i=0 ; i<2 ; i++) {
for (int j = 0; j < 10; j++) {
System.out.println("i的值是"+i+",j的值是"+j);
if(j>3){
continue outer;
}
}
}
}
}
运行结果是:
i的值是0,j的值是0
i的值是0,j的值是1
i的值是0,j的值是2
i的值是0,j的值是3
i的值是0,j的值是4
i的值是1,j的值是0
i的值是1,j的值是1
i的值是1,j的值是2
i的值是1,j的值是3
i的值是1,j的值是4
i的值是2,j的值是0
i的值是2,j的值是1
i的值是2,j的值是2
i的值是2,j的值是3
i的值是2,j的值是4