选择结构

你好,欢迎回来,我是BoBo!HAKUNA MATATA!!!

在数据和运算的基础上,现在又有了新的需求:流程控制。代码的执行流程一般不会一竿子到底的执行下去,很多时候会根据数据的变化选择合适的走向,甚至改变程序的运行结果。从这节课开始,我为你介绍 Java 语言的流程控制结构,包括选择结构循环结构两部分。

程序的结果跟代码的执行顺序紧密相关,通过使用一些特定的语句控制代码的执行顺序从而完成特定的功能,这就是流程控制结构。其实,Java 程序的流程控制,不仅仅是选择结构和循环结构这两种,还有一种默认的结构,叫顺序结构,即程序按照代码的顺序从上往下、从左往右依次执行的代码结构。顺序结构是最简单的流程控制结构,不需要特定的控制语句,程序的大多数代码都是这样执行的:

这种结构过于简单,就不需要花费篇幅去介绍了。

这门课介绍选择结构,下门课介绍循环结构。

选择结构也叫判断结构/分支结构,也就是说,根据判断条件是否成立,选择执行对应的分支。所以,选择结构一般由两大部分组成:判断条件和分支。判断条件和分支都有可能会有多个,根据不同的条件去执行对应的分支,完成复杂的业务逻辑。通过本节课程的学习,你将能够:

​ 使用 if 语句来控制你的 Java 程序的执行流程

​ 使用 switch 语句来控制你的 Java 程序的执行流程

​ 使用扫描器(Scanner)对象在控制台输入数据

课程内容如下:

  • if语句:如果爱,请深爱
  • ​ Scanner扫描器:我是数据探险家
  • ​ switch语句:排排坐,吃果果

第一关 if语句:如果爱,请深爱

1.1 选择结构的概念和if格式

选择结构要先判断条件是否成立,条件成立则执行一部分代码,不成立则执行另外一部分或结束代码,所以选择结构也叫判断结构。判断条件一般是一个或多个关系表达式,当然,也不排除复杂的逻辑表达式,因为它们执行的结果都是 boolean 类型的值。

看一段代码:

public class Test{
    public static void main(String[] args) {
        System.out.println("开始执行");
        // 定义两个int类型的变量,它们的值都是10
        int a = 10;
        int b = 10;
    
        if (a == b) { // 判断两个变量是否相等,很明显,条件是成立的
            // 如果能走这里, 说明条件成立.
            System.out.println("a和b是相等的");
        }
        System.out.println("结束执行");
    }
}

这段代码的开始和结束分别打印了一些提示信息,中间才是我们关注的内容。关键字 if 之后是一对小括号,里面是一个关系表达式,然后紧跟着一对大括号(花括号),这就是 if 语句的基本格式,它意味着:如果 if 之后小括号里关系表达式返回结果为 true,那么就执行大括号里面的代码——一对大括号包起来的代码叫做代码块,否则就跳过大括号里面的代码,直接执行最后的输出语句“结束执行”。很明显,a == b 是成立的,所以,这段代码的输出结果是:

开始执行
a和b是相等的
结束执行

反之,如果把 a == b 改成这样的条件 a > b,判断条件将不成立,

public class Test{
    public static void main(String[] args) {
        System.out.println("开始执行");
        // 定义两个int类型的变量,它们的值都是10
        int a = 10;
        int b = 10;

        if (a > b) { // 判断两个变量是否相等,很明显,条件是成立的
            // 如果能走这里, 说明条件成立.
            System.out.println("a和b是相等的");
        }
        System.out.println("结束执行");
    }
}

执行结果就变成了:

开始执行
结束执行

从上面代码的运行效果可以得到以下两个结论,第一,if 语句的基本格式是:

     // 第一种格式:
    if (关系表达式) {
        // 语句体
    }

第二,它的执行流程:

也就是说,当关系表达式返回结果为 true 时,就会执行 if 代码块里面的语句体,否则,程序就跳过了 if 语句,直接执行到结束。这是最简单的 if 语句格式,它代表了“如果——那么”的执行逻辑。

考一考:if 语句使用

看程序,说结果:

public class Test{
    public static void main(String[] args) {
        int x = 1, y = 1;
        if (x++ == 2 && ++y == 2) {
            x = 7;
        }
        System.out.println("x = " + x + ", y = " + y);
    }
}    

答案:x = 2, y = 1

1.2 if 和 else格式

如果爱,请深爱,否则,请放开。条件不成立,并不代表程序就一定该结束。这样的结构怎么实现呢?再看一段代码:

public class Test{
    public static void main(String[] args) {
        System.out.println("开始执行");
        // 定义两个int类型的变量,它们的值都是10
        int a = 10;
        int b = 10;
    
        if (a == b) { // 如果爱
            System.out.println("请深爱");
        } else { // 否则
            System.out.println("请放开");
        }
        System.out.println("结束执行");
    }
}

if 语句的代码块之后,我又加了一个 else 代码块。此时 a == b 的条件是成立的,会打印出“请深爱”,但是 else 代码块的内容不会输出。

开始执行
请深爱
结束执行

else 代码块没有执行。现在,我们如法炮制,还是把判断条件改了,变成:a > b,这样条件就不成立了,else 代码块会执行吗?看看输出结果:

public class Test{
    public static void main(String[] args) {
        System.out.println("开始执行");
        // 定义两个int类型的变量,它们的值都是10
        int a = 10;
        int b = 10;
    
        if (a > b) { // 如果爱
            System.out.println("请深爱");
        } else { // 否则
            System.out.println("请放开");
        }
        System.out.println("结束执行");
    }
}
开始执行
请放开
结束执行

Bingo!if 代码块和 else 代码块是互斥的,我发现了一个宝藏!

没错,这里的 ifelse 组成了一个完整的选择结构,它们只可能执行其中的一个分支,永远不可能同时执行两个部分。那么,根据上面的代码,我们得到了 if 语句的第二种格式和它的执行流程:

    // 第二种格式:
    if (关系表达式) {
        // 语句体1
    } else {
        // 语句体2
    }

执行流程:

如果 if 语句的关系表达式返回结果为 true,那么就执行紧跟在 if 之后的代码块,也就是“语句体1”,否则,执行 else 之后的代码块,也就是“语句体2”,最后程序运行结束。这是 if 语句的第二种格式,它代表了“如果——那么——否则”的执行逻辑。

“看着这个节奏,if 语句好像还没介绍完。。。”

“你真聪明。”

1.3 else if格式

人工智能这么火爆,我们也研发了一个智能机器人“小黑同学”。小黑同学既聪明,又有礼貌,无论你什么时候去聊骚她,她都会先根据现在的时间跟你打招呼。比如现在是0-12点之间,她就会跟你说“上午好”,13-18点之间,她就会说“下午好”,如果是18-24点之间,她就会说“晚上好”,如果你随意的跟她说个不在这些范围的时间,她还会生气呢!不信你试试:

public class Test{
    public static void main(String[] args) {
        // 定义变量, 记录当前时间
        int time = 18; 

        // 小黑根据你的时间和你打招呼.
        if (time >= 0 && time <= 12 ) { 
            System.out.println("小黑同学说: 上午好");
        } else if (time >= 13 && time <= 18) {
            System.out.println("小黑同学说: 下午好");
        } else if (time >= 19 && time <= 24) {
            System.out.println("小黑同学说: 晚上好");
        } else {
            // 你说的时间太随意,人家会生气!
            System.out.println("小黑同学说: 买了否冷, 我不认识这个时间!");
        }
    }
}

输出结果:

小黑同学说: 下午好

if 见过了,else 也见过了,但是 else if 没见过,看起来还很高大上的样子呢!

else if 的字面意思是“否则,如果”,也就是当前面的 if 条件不成立,就看看这个 else if 后面的条件,如果还不成立,就继续往下找后面的 else if 条件,任意一个 if 或者 else if 判断成立,就会执行里面的代码,然后跳出整个 if 选择结构,否则,就一直到所有的判断都不成立,才会去执行最后的 else 后面的代码块,比如把变量 time 的值改成 -1 或者 25,

public class Test{
    public static void main(String[] args) {
        // 定义变量, 记录当前时间
        int time = -1; 

        // 小黑根据你的时间和你打招呼.
        if (time >= 0 && time <= 12 ) { 
            System.out.println("小黑同学说: 上午好");
        } else if (time >= 13 && time <= 18) {
            System.out.println("小黑同学说: 下午好");
        } else if (time >= 19 && time <= 24) {
            System.out.println("小黑同学说: 晚上好");
        } else {
            // 你说的时间太随意,人家会生气!
            System.out.println("小黑同学说: 买了否冷, 我不认识这个时间!");
        }
    }
}

执行结果就变成了:

小黑同学说: 买了否冷, 我不认识这个时间!

而其它的时间范围(0-24),这段代码就会输出对应的话。

上面的代码使用了 if 语句的第三种格式:

    // 第三种格式:
    if (关系表达式1) {
        // 语句体1
    } else if (关系表达式2) {
     // 语句体2
    }  // …
    else {
      // 语句体n+1
    }

这种格式看起来有些复杂,但是理解起来并不难,上个图,就更容易理解这个格式的执行流程了:

我把这个流程图表示的意思写下来,以便你对照着理解:

执行流程:
    先判断关系表达式1, 看其是否成立(true:成立, false:不成立).
     成立,   则执行语句体1,
     不成立, 则判断关系表达式2, 看其是否成立.
         成立, 执行语句体2,
         不成立, 则判断关系表达式3, 看其是否成立.
 ...
 依次类推,判断所有的条件,如果关系表达式n成立, 则执行语句体n, 

    否则执行语句体n + 1

一定要注意的是,无论 if 语句整个结构中的条件有多少个,每一个条件都是并列关系,它们只能执行其中一个,或者执行最后的 else 分支。还有就是,通过 if 语句的多种格式发现,除了第一个 if 判断条件之外,其它的 else ifelse 代码块都是可以省略的,它们都是“非必须的”,在你编写代码的时候,到底要不要使用那么多的条件,以及要不要 else 分支,完全取决于:你的心情。

此外,if 语句的判断条件或者 else 后面的代码块,有的时候内容非常简单——只有一行代码,这个时候,可以省略大括号:

public static void main(String[] args) {
    if (5 > 3) // 代码块中只有一行代码时,可以省略大括号,但是不建议这么做
        System.out.println("5大于3成立");
    else 
        System.out.println("5大于3不成立");
}

虽然从语法的角度上可以这样做,但是有经验的开发者们,却不建议我们这样做,因为代码块里随时有可能增加新的内容,如果忘记添加上大括号,那将是一场灾难。

考一考:if 语句使用

1. 需求:给定两个变量,使用if 语句判断较大值,并输出相应信息:

    int a = 3, b = 5;

答案:

public class Test{  
    public static void main(String[] args) {
        int a = 3, b = 5;
        if (a > b) {
            System.out.println("a 大于 b");
        } else if (a == b) {
            System.out.println("a 等于 b");
        } else {
            System.out.println("a 小于 b");
        }
    }
}

2. 需求:根据学生成绩输出对应级别

  • 90-100 皇帝
  • 80-90 宰相
  • 70-80 大臣
  • 60-70 县官
  • 60以下 草民

答案:

public class Test{ 
    public static void main(String[] args) {
        int score = 83;   // 定义学生的成绩

        // 根据学生的成绩, 和题设的条件进行对比, 并按照要求进行输出即可.
        if (score >= 90 && score <= 100) {
            System.out.println("皇帝");
        } else if (score >= 80 && score < 90) {
            System.out.println("宰相");
        } else if (score >= 70 && score < 80) {
            System.out.println("大臣");
        } else if (score >= 60 && score < 70) {
            System.out.println("县官");
        } else if (score >= 0 && score < 60) {
            System.out.println("草民");
        } else { // 注意非法数据的处理
            System.out.println("没有这样的成绩, 你是从火星来的吧?");
        }
    }
}


第二关 Scanner扫描器:我是数据探险家

实际开发中的数据比如用户登录信息,订单状态等等,几乎都是动态获取的,而不是像我们上面的代码中直接定义成固定值:int score = 83;。虽然现在我们还无法直接像生产环境那样获取数据,但是 Java 语言给我们提供了一个可供模拟的工具:ScannerScanner是一个扫描器,它可以扫描我们在控制台录入的数据,因此,我们可以通过它来模拟动态获取数据的效果。

那么,如何使用 Scanner 呢?

Scanner 在 Java 语言中是一个类(关于类的概念,到【面向对象】章节详细介绍),你可以把它看成是具备一定功能的一个工具,它的功能就是:接收我们从控制台录入的数据,然后把这些数据交给程序去处理。使用 Scanner 进行控制台录入数据,需要三步:

​ 第一步:导包。其实是导入扫描器这个类

    import java.util.Scanner; // 导入扫描器这个类

​ 第二步:创建扫描器对象。扫描器这个类,需要以对象的形式工作

    // 扫描器对象的名字叫 sc,你也可以根据标识符的命名规则去定义其它的名字,比如 scanner、next 等等
    Scanner sc = new Scanner(System.in);

​ 第三步:接收数据。使用 对象.方法名() 这种格式来获取我们录入的数据,并将结果赋值给一个变量。

    int a = sc.nextInt();

写一段代码试试效果:

// 1. 导包
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
        // 2. 创建键盘录入对象.
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入一个整数: "); // 加入一句提示信息
        // 3. 接收数据.
        int i = sc.nextInt();

        // 将用户录入的数据打印到控制台上.
        System.out.println("i: " + i);
    }
}

使用 Scanner 的三个步骤缺一不可。第一、二步都是固定格式,除了扫描器的名字 sc 可以改成你喜欢的标识符之外,其它的代码都不能改动,而且,尤其要注意第一步导包代码的位置:在类的上面,而第二、三步都在 main 方法里面。

程序执行到第三步的时候,控制台会显示闪烁的光标,此时程序正在等待我们录入数据(整个程序是阻塞的):

此时,我们可以使用键盘录入数据,问题是:录入什么数据呢?nextInt() 方法的意思是“从控制台获取一个 int 类型的整数”,所以,我们可以输入一个数字试试:

很快,控制台就把我们输入的数字10给重新打印出来了。这个过程发生了什么?回到上面代码:当我们在控制台输入10之后,按回车键,此时,nextInt() 方法扫描到了这个数据,然后把它赋值给变量 i ,紧接着,输出语句把变量 i 的值重新打印出来了。这就是扫描器 Scanner 的使用。

回到上面“根据学生成绩输出对应级别”的案例,现在我们可以从控制台录入学生成绩,接收到数据之后,再用 if 语句进行判断:sf

// 1. 导包
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
        // 2. 创建键盘录入对象.
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个整数: ");
        // 3. 接收数据.
        int score = sc.nextInt(); // 输入学生的成绩
        // 根据学生的成绩, 和题设的条件进行对比, 并按照要求进行输出即可.
        if (score >= 90 && score <= 100) {
            System.out.println("皇帝");
        } else if (score >= 80 && score < 90) {
            System.out.println("宰相");
        } else if (score >= 70 && score < 80) {
            System.out.println("大臣");
        } else if (score >= 60 && score < 70) {
            System.out.println("县官");
        } else if (score >= 0 && score < 60) {
            System.out.println("草民");
        } else { // 注意非法数据的处理
            System.out.println("没有这样的成绩, 你是从火星来的吧?");
        }
    }
}

你可能会说,学生成绩不一定就是整数啊?!没错,如何录入浮点数呢?还有,可以从控制台录入我想说的话吗?

答案是肯定的。前面学习到的 Java 语言的八种基本数据类型,除了 char 类型之外,都有对应的接收方式。如果想接收 char 类型数据,可以使用 int 类型的变量来接收,然后再 强制类型转换char 类型;如果想接收你对我爱的表白,就要使用 String 类型了,因为那可能是超过一万字的小作文:

nextByte() :接收 byte 类型的数据。用法示例:byte by = sc.nextByte();

nextShort() :接收 short 类型的数据。用法示例:short sh = sc.nextShort();

nextInt() :接收 int 类型的数据。用法示例:int i = sc.nextInt();

nextLong() :接收 long 类型的数据。用法示例:long lo = sc.nextLong();

nextFloat() :接收 float 类型的数据。用法示例:float fl = sc.nextFloat();

nextDouble() :接收 double 类型的数据。用法示例:double dou = sc.nextDouble();

nextBoolean() :接收 boolean 类型的数据。用法示例:boolean bo = sc.nextBoolean();

nextLine() :接收 String 类型的数据。用法示例:String line = sc.nextLine();

最后,还需要注意的一个点是,当我们在控制台录入的数据类型与扫描器对象使用的方法期待的类型不匹配,这个时候程序就会报错。比如sc.nextInt() 这行代码期待的是 int 类型,如果我们输入的是字符串,就会变成这样的结果:

程序报出了一个错,并终止。虽然大部分错误信息我们看不懂,但是通过 InputMismatchException 这行信息的字面意思,大概也能知道是“输入不匹配”的问题。准确地说,程序抛出了一个异常(Exception)并终止,这个异常的名字叫做 InputMismatchException ,异常信息的最后一行,指示出我们的代码的第12行出现了问题:int i = sc.nextInt();,意思已经非常明确了,就是在录入数据的时候发生了类型不匹配的现象。现阶段暂时不要求你对“异常”的概念了解更多,你只需要知道现在程序报错了,是因为输入类型与使用的扫描器方法需要的类型不一致导致的,而且要尽量避免这样的情况发生。

考一考:Scanner 扫描器使用

1. 需求:编写代码,键盘录入两个整数,求它们的和

实现键盘录入功能的步骤:

第一步:导包。

第二步:创建扫描器对象。

第三步:接收数据。

答案:

// 1. 导包
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
        // 需求: 键盘录入两个整数, 求它们的和
        // 2. 创建键盘录入对象.
        Scanner sc = new Scanner(System.in);

        // 3. 提示用户录入数据, 并接收.
        System.out.println("请录入第一个整数: ");
        int a = sc.nextInt();
        System.out.println("请录入第二个整数: ");
        int b = sc.nextInt();

        // 4. 计算整数和.
        int sum = a + b;

        // 5. 打印结果.
        System.out.println("sum: " + sum);
    }
}

2. 需求:键盘录入两个数据,获取这两个数据的较大值

思路分析:

​ A:键盘录入的三个步骤:

​ 导包;创建键盘录入对象;获取数据;

​ B:获取两个数据的较大值:if 语句的格式2实现

​ C:把较大的结果输出

答案:

import java.util.Scanner;

public class IfDemo {
    public static void main(String[] args) {
        // 需求: 键盘录入两个数据, 获取这两个数据的最大值
        // 1. 创建键盘录入对象, 以便接收用户录入的数据(包含: 导包, 创建对象)
        Scanner sc = new Scanner(System.in);

        // 2. 提示用户录入两个整数, 并接收
        System.out.println("请录入第一个整数: ");
        int a = sc.nextInt();
        System.out.println("请录入第二个整数: ");
        int b = sc.nextInt();

        // 3. 定义变量, 记录最大值
        int max;

        // 4. 通过if语句的第二种格式, 判断两个整数的最大值
        if (a >= b) {
            // 如果走这里, 说明a大
            max = a;
        } else {
            // 如果走这里, 说明b大
            max = b;
        }

        // 5. 将结果打印到控制台上
        System.out.println("较大值是: " + max);
    }
}


第三关 switch语句:排排坐,吃果果

if 语句可以适用几乎所有的开发场景,但是,当每个分支的判断条件都是具体值的时候,使用 if 语句就不如使用 switch 语句更加直观。比如,判断1-7的整数对应的星期,使用 if 语句和 switch 语句分别来实现:

先看 if 语句的效果:

public class Test{
    public static void main(String[] args) {
        int weekDay = 1;
        if (weekDay == 1) {
            System.out.println("星期一");
        } else if (weekDay == 2) {
            System.out.println("星期二");
        } else if (weekDay == 3) {
            System.out.println("星期三");
        } else if (weekDay == 4) {
            System.out.println("星期四");
        } else if (weekDay == 5) {
            System.out.println("星期五");
        } else if (weekDay == 6) {
            System.out.println("星期六");
        } else if (weekDay == 7) {
            System.out.println("星期日");
        } else {
            System.out.println("我不认识这样的时间, 你是从火星来的吧?");
        }
    }
}

再来看看使用 switch 语句的效果:

public class Test{    
    public static void main(String[] args) {
        int weekDay = 1;
        switch (weekDay) {
           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;
           case 6:
               System.out.println("星期六");
               break;
           case 7:
               System.out.println("星期日");
               break;
           default:
               System.out.println("我不认识这样的时间, 你是从火星来的吧?");
               break;
       }
    }
}

这是 switch 语句的标准格式,两段代码的功能是完全等价的。仔细观察,使用 switch 语句时,更容易观察到判断条件的具体值和整段代码的清晰结构,这也是之所以出现 switch 语句的原因。在实际开发中,所有的 switch 结构都可以替换成 if 结构,反之不成立。切记:switch 只适用于判断具体值的分支结构,不适用于判断某一范围,而 if 都可以。这段代码的执行流程跟 if 结构几乎是完全一样的:

回过头来再对比 if 语句来看下 switch 语句的格式和各部分的含义:

    switch (表达式)  {   // 表达式的取值类型:byte,short,int,char JDK5以后可以是枚举;JDK7以后可以是String
        case 值1:       // case 后面跟的是要和表达式进行比较的值,每一个 case 代表一个分支
            语句体1;     // 语句体部分可以是一条或多条语句
            break;      // 表示中断,结束的意思,可以结束switch语句
        case 值2:
            语句体2;
            break;
            // ...      // case 可以有很多个
        default:        // 表示所有情况都不匹配的时候,就执行这里的内容,相当于 if 语句的 else 部分
            语句体n+1;
            break;
    }

switch 关键字相当于 if 关键字,只不过,if 后面小括号里是关系表达式,而 switch 后面小括号里一般是一个变量,而变量的值,与后续每个 case 分支的值进行对比,也就是把“比较”的动作延后了。switch 后面的“表达式”既然是一个变量,那么它是什么数据类型呢,所有类型都可以吗?答案是否定的。前面我们提到,switch 适合判断固定值的情况,也就意味着这些值必须是已知的,换句话说,这些值都应该是常量,比如字面值常量(byteshortcharint)、String枚举 等类型。String 类型的每个数据都是字符串常量,所以它可以作为 switch 的表达式类型(JDK7新特性);枚举 是Java 语言默认支持的一种常量类,所以它也可以作为 switch 的表达式类型(JDK5新特性)。而 Java 语言的八种基本数据类型里,booleanlongfloatdouble 暂时都不被支持,主要是因为这些类型的数据作为条件进行分支选择的场景太少,但不排除在后续 JDK 版本里提供相应支持的可能。

case 代表每一个具体的分支,它后面跟具体的值,这里的取值其实是前面“表达式”变量可能的值。如果匹配成功,则执行这个 case 里面的语句体,否则,继续进行下个 case 的判断,直到任意一个 case 匹配成功,或者执行 default 部分,default 分支相当于 if 结构最后的 else。语句体可以有多句,就像 if 后面的代码块一样,业务逻辑可以很复杂,也可以很简单。

每个 case 的最后一般都会有个 break 语句,代表这个分支的结束,它的作用类似 if 结构的代码块结束标记,程序执行到 break 语句,将会跳出整个 switch 结构。同样的,就像 if 语句的大括号可以省略一样,break 语句也不是必须的,二者有两点区别:第一,if 语句当且仅当代码块只有一行语句时才可以省略大括号,而 case 分支没有这个限制,它可以在拥有很多代码的前提下,省略 break 语句;第二,在 if 语句中,执行完分支的代码就一定会跳出 if 结构,而如果省略了 case 中的 break 语句,执行完当前分支后并不会跳出 switch,而是继续执行后续的 case 或者 default 分支,直到遇到 break 语句跳出,或者执行完 switch 结构剩余的所有代码。这种情况适用于多个取值执行相同的业务逻辑的场景。举个栗子:

需求:输入1-12月份数字,输出对应的季节,如果输入错误,则打印提示信息

春季:3,4,5;

夏季:6,7,8;

秋季:9,10,11;

冬季:12,1,2;

完整代码:

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入月份(1-12):");
        int month = sc.nextInt();
        switch (month) {
            case 3: // 此分支没有任何内容,也没有 break 语句。下同
            case 4:
            case 5:
                System.out.println("春季");
                break;
            case 6:
            case 7:
            case 8:
                System.out.println("夏季");
                break;
            case 9:
            case 10:
            case 11:
                System.out.println("秋季");
                break;
            case 12:
            case 1:
            case 2:
                System.out.println("冬季");
                break;
            default:
                System.out.println("你说的是火星月吗?");
                break;
        }
    }
}

这段代码中,当 case 分支取值为3、4、6、7、9、10、12、1 等数字的时候,不仅没有任何代码,也没有 break 语句,程序怎么执行的呢?当我们从控制台输入数字3,匹配第一个 case 分支成功,则执行该分支里面的代码——尽管没有任何内容,执行完该分支,由于没有 break 语句,此时程序并不会跳出 switch 结构,而是会继续执行后续分支的代码,这就到了 case 4: 这一行,现在代码还需要再做一次判断吗?答案是:不需要了。这就好比你拿着一把钥匙,打开了仓库的某一扇门,进去之后整个仓库的金银珠宝都出现在你面前,让你随便拿,难道你还会出去从别的门再进一次?下面这张图也许有助于你理解这段代码的执行流程:

整个 switch 是一个仓库,每个 case 分支都是一扇进入仓库的门,default 分支是后门,而表达式则是用来打开门的钥匙,表达式的值与任何一扇门匹配,则可以打开这扇门进入仓库,然后金银珠宝随你造。所以,匹配 case 3: 成功之后,由于没有执行 break 语句,那么就继续执行 case 4case 5 两个分支,直到遇到 case 5break 语句,退出 switch 结构。

switch 语句后半部分内容有些难,需要你仔细阅读这部分知识,并实际运行这些代码才能够理解。

考一考:switch 语句使用

1. 看程序,说结果:

public static void main(String[] args) {
    int x = 2, y = 3;
    switch (x) {
        default:
            y++;
        case 3:
            y++;
            break;
        case 4:
            y++;
    }
    System.out.println("y=" + y);
}

答案:y=5

2. 实现一个简单的计算器,支持加、减、乘、除四个功能。

例如:输入数字3和5,相加结果是8。(小提示:String 类型的默认值的空常量 null,可以用来判断 String 变量是否被重新赋值)

控制台数据格式:

​ 请输入第一个数字:

​ 3

​ 请输入第二个数字:

​ 5

​ 请按序号输入您的操作(1加;2减;3乘;4除):

​ 1

​ 3 + 5 的结果是 8

答案:

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第一个数字:");
        int num1 = sc.nextInt();
        System.out.println("请输入第二个数字:");
        int num2 = sc.nextInt();
        System.out.println("请按序号输入您的操作(1加;2减;3乘;4除):");
        int opr = sc.nextInt();

        int result = 0;
        String operate = null; // 字符串类型的默认值,后面如果重新赋值了,说明执行了正确的操作符
        switch (opr) {
            case 1:
                result = num1 + num2;
                operate = " + ";
                break;
            case 2:
                result = num1 - num2;
                operate = " - ";
                break;
            case 3:
                result = num1 * num2;
                operate = " * ";
                break;
            case 4:
                result = num1 / num2;
                operate = " / ";
                break;
            default:
                System.out.println("输入错误,不支持的操作符:" + opr);
                break;
        }
        if (operate != null) { // 用于判断任意 case 代码正常执行了,而不是执行了 default
            System.out.println(num1 + operate + num2 + " 的结果是:" + result);
        }
    }
}


课程总结

最后,对这节课的内容做个总结:

  • if 语句的三种格式和它们的执行流程:
     // 第一种格式:
    if (关系表达式) {
        // 语句体
    }

    // 第二种格式:
    if (关系表达式) {
        // 语句体1
    } else {
        // 语句体2
    }

    // 第三种格式:
    if (关系表达式1) {
        // 语句体1
    } else if (关系表达式2) {
        // 语句体2
    }  // …
    else {
        // 语句体n+1
    }
  • switch 语句的标准格式和执行流程:
    switch (表达式)  {   // 表达式的取值类型:byte,short,int,char JDK5以后可以是枚举;JDK7以后可以是String
        case 值1:       // case 后面跟的是要和表达式进行比较的值,每一个 case 代表一个分支
            语句体1;     // 语句体部分可以是一条或多条语句
            break;      // 表示中断,结束的意思,可以结束switch语句
        case 值2:
            语句体2;
            break;
            // ...      // case 可以有很多个
        default:        // 表示所有情况都不匹配的时候,就执行这里的内容,相当于 if 语句的 else 部分
            语句体n+1;
            break;
    }
  • if 语句适合做区间值/范围的判断,而 switch 适合做固定值的判断;
  • switch 语句需要判断的所有值都是常量,且在加载 switch 结构的代码时就已经完成初始化,所以,switch 效率更高;
  • 写程序的时候,做数据测试,应该测试这样的几种情况:正确数据错误数据边界数据

博学谷
27 声望5 粉丝

有趣易学,人人都可以学编程