Java 零基础教程

Java switch 语句

switch 语句提供了一种极其优雅的方式,让程序能够根据单个变量的值来执行不同的代码块。当你需要处理一组固定的、已知可能值时,它是冗长乏味的 if-else if-else 链的完美替代品。

掌握 switch 语句对于编写高效且易读的代码至关重要,它能让你以一种高度结构化的方式来应对多重条件分支。

1. 理解 switch 语句

Java 中的 switch 语句允许你根据一个表达式(通常是一个变量)的值,从多个代码块中选择一个来执行。这个表达式只会被评估一次,然后它的值会依次与各个 case 标签中的值进行精确比对。如果找到了匹配项,就会执行该 case 下属的代码块。

1.1 基础语法结构

switch 语句的基本语法如下:

switch (expression) {
    case value1:
        // 如果 expression == value1,执行这里的代码;
        break;
    case value2:
        // 如果 expression == value2,执行这里的代码;
        break;
    case valueN:
        // 如果 expression == valueN,执行这里的代码;
        break;
    default:
        // 如果 expression 没有任何匹配项,执行这里的保底代码;
}
  • switch (expression): 括号里的 expression (表达式) 是你要检测的目标。它的数据类型被严格限制为:byteshortintcharStringenum (枚举)。
  • case value1:: 每个 case 标签指定一个具体的值。如果 expression 的值与 value1 完全一致,程序就会从这个标签往下执行代码。
  • break;break 语句极其关键。它的作用是立即终止 switch 语句,防止代码像瀑布一样“穿透”执行到下一个 case。如果没有它,程序会无视后续的条件匹配,直接执行下一个 case 里的代码。
  • default:default 标签是可选的(但强烈建议加上)。如果 expression 的值没有匹配中上面的任何一个 case,程序就会执行 default 后面的代码,类似于 if-else 中的最后一个 else

2. break 的重要性:警惕“穿透效应” (Fall-through)

break 语句是控制 switch 内部执行流的开关。如果没有它,程序就会发生“穿透效应 (Fall-through)”,直接滑入下一个 case 并执行其代码,哪怕下一个 case 的值根本不匹配!

虽然在极少数极客场景下这可能是有意为之,但在 99% 的日常开发中,漏写 break 是导致重大 Bug 的元凶。

来看看这个错误示例

int day = 2;
String dayType;

switch (day) {
    case 1:
        dayType = "星期一";
    case 2:
        dayType = "星期二";
    case 3:
        dayType = "星期三";
    default:
        dayType = "工作日";
}

System.out.println(dayType); // 输出: 工作日 (糟糕!发生了穿透)

解析: 在这个例子中,由于没有任何 break 语句阻拦,代码匹配到 case 2 后,为 dayType 赋值了 "星期二"。但它没有停下,而是继续向下穿透执行了 case 3,最后又穿透执行了 default。最终 dayType 被覆盖成了 "工作日",这显然违背了我们的初衷。

正确写法:在每个 case 后补上 break

int day = 2;
String dayType;

switch (day) {
    case 1:
        dayType = "星期一";
        break; // 匹配成功并执行后,立即跳出 switch
    case 2:
        dayType = "星期二";
        break;
    case 3:
        dayType = "星期三";
        break;
    default:
        dayType = "工作日";
}

System.out.println(dayType); // 输出: 星期二 (完美!)

3. switch 支持的数据类型实战

正如前文所述,switch 表达式仅支持特定数据类型。让我们来看看具体的实战演示:

3.1 整数型 (int)

int month = 4;
String monthString;

switch (month) {
    case 1:  monthString = "一月"; break;
    case 2:  monthString = "二月"; break;
    case 3:  monthString = "三月"; break;
    case 4:  monthString = "四月"; break;
    // ... 省略 5 到 11 月 ...
    case 12: monthString = "十二月"; break;
    default: monthString = "无效月份";
}
System.out.println(monthString); // 输出: 四月

3.2 字符型 (char)

char grade = 'B';
String gradeDescription;

switch (grade) {
    case 'A': gradeDescription = "优秀"; break;
    case 'B': gradeDescription = "良好"; break;
    case 'C': gradeDescription = "中等"; break;
    case 'D': gradeDescription = "及格"; break;
    case 'F': gradeDescription = "不及格"; break;
    default: gradeDescription = "无效等级";
}
System.out.println(gradeDescription); // 输出: 良好

3.3 字符串型 (String)

String day = "Monday";
String activity;

switch (day) {
    case "Monday":    activity = "一周的开始"; break;
    case "Tuesday":   activity = "第二天"; break;
    case "Wednesday": activity = "周中"; break;
    case "Thursday":  activity = "快熬出头了"; break;
    case "Friday":    activity = "周末马上到"; break;
    default:          activity = "周末活动";
}
System.out.println(activity); // 输出: 一周的开始

3.4 枚举型 (enum)

首先,在类外部或内部定义一个 enum(枚举):

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

然后,在 switch 中丝滑使用它:

Day today = Day.WEDNESDAY;
String message;

switch (today) {
    case MONDAY:    message = "打工人开启新一周"; break;
    case TUESDAY:   message = "工作第二天"; break;
    case WEDNESDAY: message = "小周末来啦!"; break;
    case THURSDAY:  message = "疯狂星期四"; break;
    case FRIDAY:    message = "感谢老天,今天是周五!(TGIF!)"; break;
    case SATURDAY:  message = "周末万岁!"; break;
    case SUNDAY:    message = "慵懒的周日"; break;
    default:        message = "无效的日子";
}
System.out.println(message); // 输出: 小周末来啦!

4. 默认分支:default 的作用

default 分支是 switch 语句的可选部分。当所有的 case 都无法匹配目标值时,它提供了一个“兜底/后备”选项。养成写 default 的好习惯,能帮助你优雅地处理无效的输入或未知的异常情况,让代码极其健壮。

int number = 10;
String result;

switch (number) {
    case 1:  result = "一"; break;
    case 2:  result = "二"; break;
    case 3:  result = "三"; break;
    default: result = "其他数字";
}
System.out.println(result); // 输出: 其他数字 (因为 10 没有被 case 命中)

5. 进阶特性:Switch 表达式 (Java 12+)

如果你使用的是 Java 12 及以上版本,恭喜你,Java 对 switch 进行了史诗级增强,推出了 Switch 表达式 (Switch Expressions)。它不仅让代码更紧凑,还能直接返回值

int day = 2;
// 直接将 switch 的结果赋值给 dayType 变量
String dayType = switch (day) {
    case 1 -> "星期一";
    case 2 -> "星期二";
    case 3 -> "星期三";
    default -> "工作日";
};

System.out.println(dayType); // 输出: 星期二

Switch 表达式的杀手级优势:

  • 箭头标签 (->): 替代了传统的冒号 :
  • 彻底告别 break: 箭头语法天然免疫穿透效应,匹配完毕直接返回值并退出,再也不用手动写 break 了!
  • 强制穷举 (Exhaustiveness): 编译器会强制要求你处理所有可能的情况(对于枚举必须列全,对于普通数据类型必须加 default),消除了漏写逻辑的隐患。
  • 直接返回值: 箭头右侧的内容就是该表达式返回的结果。

如果你需要在匹配后执行一段复杂的代码(超过一行),你可以使用大括号 {},并通过 yield 关键字来返回值:

int day = 5;
String dayType = switch (day) {
    case 1 -> "星期一";
    case 2 -> "星期二";
    case 3 -> "星期三";
    case 4 -> "星期四";
    case 5 -> {
        System.out.println("周末马上就到啦!");
        yield "星期五"; // 使用 yield 关键字返回值
    }
    default -> "无效的日子";
};
System.out.println(dayType); 
// 依次输出: 
// 周末马上就到啦!
// 星期五

6. 实战演练与演示

为了巩固知识,让我们来看几个真实的业务场景代码。

6.1 示例 1:简易计算器

利用 switch 处理基本的数学运算符。

import java.util.Scanner;

public class Calculator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("请输入第一个数字: ");
        double num1 = scanner.nextDouble();
        
        System.out.print("请输入运算符 (+, -, *, /): ");
        char operator = scanner.next().charAt(0);
        
        System.out.print("请输入第二个数字: ");
        double num2 = scanner.nextDouble();
        
        double result = 0.0;
        
        switch (operator) {
            case '+':
                result = num1 + num2;
                break;
            case '-':
                result = num1 - num2;
                break;
            case '*':
                result = num1 * num2;
                break;
            case '/':
                if (num2 != 0) {
                    result = num1 / num2;
                } else {
                    System.out.println("错误:除数不能为零。");
                    return; // 结束程序
                }
                break;
            default:
                System.out.println("无效的运算符。");
                return;
        }
        
        System.out.println("计算结果: " + result);
        scanner.close();
    }
}

6.2 示例 2:利用穿透效应合并分支 (月份判断季节)

刚才说穿透效应是 Bug,但如果我们故意利用它,就能让多个不同的值共享同一段代码!

public class SeasonChecker {
    public static void main(String[] args) {
        int month = 8;
        String season;
        
        switch (month) {
            case 12: case 1: case 2: // 12、1、2月走到一起
                season = "冬季";
                break;
            case 3: case 4: case 5:
                season = "春季";
                break;
            case 6: case 7: case 8:
                season = "夏季";
                break;
            case 9: case 10: case 11:
                season = "秋季";
                break;
            default:
                season = "无效的月份";
        }
        System.out.println(month + " 月对应的季节是 " + season); // 输出: 8 月对应的季节是 夏季
    }
}

6.3 示例 3:用逗号分割的现代化 Switch (判断周末)

在 Java 14+ 中,使用新的 Switch 表达式时,你可以直接用逗号把多个值写在一行里,比故意利用穿透效应更直观!

public class DayTypeChecker {
    public static void main(String[] args) {
        int day = 6; // 1: 周一, 2: 周二, ..., 7: 周日
        
        String dayType = switch (day) {
            case 1, 2, 3, 4, 5 -> "工作日";
            case 6, 7 -> "休息日";
            default -> "无效的日期";
        };
        
        System.out.println("周 " + day + " 属于 " + dayType); // 输出: 周 6 属于 休息日
    }
}