java的switch支持string是编译器在编译期实现的双重校验机制:先用hashcode()快速分流(生成tableswitch或lookupswitch),再用equals()精确匹配以防哈希冲突;case必须为编译期常量,null会直接抛npe而非进入default。

Java 的 switch 语句本身不直接支持字符串,所谓“支持 String”是编译器在编译期做的智能转换——它把字符串比较拆成两步:先靠 hashCode() 快速定位,再用 equals() 精确确认。这不是语法糖,而是有明确字节码支撑的双重校验机制。
为什么必须双重校验?单靠 hashCode 不行
因为哈希值只是整数映射,不同字符串可能产生相同哈希(哈希冲突)。比如 "Aa" 和 "BB" 的 hashCode() 都是 2112,"FB" 和 "Ea" 都是 2236。如果只比哈希,就会误跳分支。
- 编译器无法在编译期排除所有冲突,所以必须保留内容比对环节
- 每个
case分支前都会插入if (str.equals("xxx"))判断 - 只有哈希匹配 且 equals 为 true,才算真正命中
编译期就固化哈希值,运行时只算一次
所有 case 字符串必须是编译期常量(如 "abc"、static final String X = "abc"),这样编译器才能在打包时就把它们的 hashCode() 算出来,并硬编码进 class 文件。
- 运行时只调用一次
str.hashCode(),不是每个 case 都重新计算 - 后续查表(
tableswitch或lookupswitch)完全基于这个整数 - 若 case 少于 3 个,javac 可能直接退化为 if-else,不生成跳转表
null 值不会进 default,而是直接抛异常
整个双重校验流程的前提是表达式非 null。一旦 switch(str) 中的 str 为 null,str.hashCode() 就会触发 NullPointerException,根本不会走到任何 case 或 default 分支。
- 这和 int/enum switch 不同——它们对 null 是类型不匹配,编译就报错
- String switch 允许变量为 null,但运行时风险由开发者承担
- 若需处理 null,得在外层加判空,例如
if (str == null) { ... } else switch(str) { ... }
性能特点:比 if-else 稳定,但不如 int switch
虽然多了方法调用和可能的 equals 比较,但它避免了 if-else 的线性查找,平均时间接近 O(1)。
- 哈希分布密集时用
tableswitch(数组索引),稀疏时用lookupswitch(二分查找) - JIT 编译器会对高频执行的分支内联
hashCode()和部分equals() - 最坏情况仍是 O(n),比如所有 case 哈希都一样,就得逐个 equals











