java 7+ switch支持string是编译器在编译期通过“先哈希、再equals”两步实现的:先用hashcode快速分流(tableswitch/lookupswitch),再用equals精确匹配;case必须为编译期常量,null会直接抛npe。

Java 7+ 的 switch 支持 String,不是 JVM 直接支持的特性,而是编译器在编译期做的智能转换。它靠的是“先哈希、再比对”的两步走策略,缺一不可。
第一步:用 hashCode 快速分流
JVM 字节码只认整数跳转(tableswitch 或 lookupswitch),不认字符串。所以编译器会把每个 case "xxx" 的哈希值提前算好,硬编码进 class 文件。运行时只调一次 s.hashCode(),然后查表跳转到对应分支位置。
- 哈希值在编译期就确定,不会每次运行都重算
- 如果所有 case 的哈希分布密集(比如连续或接近),编译器倾向生成
tableswitch;稀疏则用lookupswitch - 哈希只是“粗筛”,不能保证唯一——比如
"Aa"和"BB"的hashCode都是 2112
第二步:用 equals 精确兜底
跳转到某个整数分支后,不代表就匹配成功了。该分支里一定包裹着一个或多个 if (s.equals("xxx")) 判断:
- 多个字符串哈希相同时(哈希冲突),它们会被编译到同一个 case 分支里,靠
equals逐个区分 - 哪怕只有一个 case,也必须加
equals,否则语义错误(比如传入哈希相同但内容不同的字符串) - 这个判断是运行时执行的,对象内容稳定、无逃逸时,HotSpot JIT 可能将其内联优化
编译期强约束:所有 case 必须是常量
编译器要能在编译阶段拿到确切字符串和它的哈希值,才能建表。所以:
- 允许:
"hello"、static final String MSG = "world" - 不允许:
new String("x")、"a" + getSuffix()、var s = "x"、变量引用(如String x = "y"; case x:) - 重复值(两个
"abc")直接报错:duplicate case label
null 值不进 default,直接抛异常
表达式为 null 时,s.hashCode() 第一步就触发 NullPointerException,根本不会走到后续分支或 default。这点和 if-else 行为不同,需主动判空防护。











