07、流程控制

  • 与任何程序设计语言一样,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、性能 对比

维度switchif-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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值