Java 7 的 String switch 和 Java 5 的枚举 switch 均为编译期语法糖,底层均转为基于 int 的 tableswitch 或 lookupswitch 指令:String switch 先调用 hashCode() 快速跳转,再用 equals() 校验防冲突;枚举 switch 则转为 ordinal() 值查静态映射表,懒加载初始化。

Java 7 开始支持 String 类型的 switch,Java 5 起就支持枚举类型。它们都不是 JVM 原生指令直接支持的,而是由编译器在编译期做转换,最终落地为基于 int 的 tableswitch 或 lookupswitch 字节码指令。
String switch 的底层实现:先 hash 再 equals
编译器会将 String 的 switch 拆成两层判断:
- 第一层:调用
String.hashCode(),生成一个int值,用tableswitch(密集)或lookupswitch(稀疏)快速跳转到候选分支; - 第二层:在每个候选分支中,用
String.equals()逐个比对原始字符串,防止哈希冲突导致误跳; - 如果所有
equals都不匹配,则跳转到default分支(或抛出异常)。
例如:switch(s) { case "a": ... case "bb": ... } 编译后相当于先算 s.hashCode(),再根据结果查表,再对命中分支做 "a".equals(s) 或 "bb".equals(s) 校验。
枚举 switch 的底层实现:转为 ordinal 值匹配
枚举类每个实例都有固定的 ordinal()(声明顺序索引,从 0 开始),编译器直接将其转为 int 值参与 switch:
- 编译时生成一个静态辅助数组(如
$SWITCH_TABLE$MyEnum),按ordinal映射到对应分支的字节码偏移; - 运行时先调用
enumVar.ordinal(),再查表跳转; - 该数组是懒加载的,首次使用时通过同步块初始化,避免多线程竞争问题。
注意:这个转换依赖 ordinal 稳定性,所以不建议在 switch 中依赖枚举值以外的字段,也不应随意调整枚举声明顺序。
为什么不用 instanceof 或 toString?
编译器没选这些方式,是因为:
-
instanceof对String无意义(全是String类型); -
toString()不安全(可能被重写、返回 null 或动态值); -
hashCode + equals是语义最准确、性能可接受、且完全符合String合同的设计; - 枚举的
ordinal是编译期确定、零开销、不可变的天然整数标识。
小结:都是编译期语法糖,不是 JVM 新特性
String 和枚举的 switch 在字节码层面都不存在独立指令。JVM 只认 int 类型的 switch。所有高级能力均由 javac 在编译阶段完成语义分析、哈希计算、校验插入和跳转表生成。这也解释了为何 String 字面量必须是编译期常量(保证 hashCode 可在编译期确定),以及为何枚举 switch 无法匹配 null(ordinal() 会 NPE,需显式判空)。

















