java switch支持string是因编译器将其转为hashcode()查表+equals()校验的整数跳转,而long因取值范围超int且无安全截断规则被jvm拒绝。

Java 的 switch 支持 String,但不是“直接比内容”,而是编译器生成哈希跳转 + 运行时 equals() 双重校验——不理解这点,就容易掉进空指针、哈希冲突、编译失败的坑里。
为什么 String 能进 switch,而 long 不行?
因为 JVM 字节码层面只认整数跳转(tableswitch / lookupswitch),switch 本质是「整数分发器」。编译器必须把非整型表达式“翻译”成整数才能用。
-
String:编译期算出每个case字面量的hashCode(),运行时先调一次str.hashCode(),再查表跳转 -
long:值范围超int,硬转会溢出,且无安全、无歧义的截断规则,JVM 拒绝这种隐式风险 - 其他引用类型(如
StringBuilder):既不能保证编译期常量,也无法统一定义hashCode()行为,编译器无法建表
编译后实际长什么样?反编译一眼看穿
写一个最简 switch:
String s = "world";
switch (s) {
case "hello": System.out.println("h"); break;
case "world": System.out.println("w"); break;
}
反编译后等价于:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
String s = "world";
String tmp = s; // 防止多次求值
int h = tmp.hashCode();
switch (h) {
case 99162322: // "hello".hashCode()
if (tmp.equals("hello")) System.out.println("h");
break;
case 113318802: // "world".hashCode()
if (tmp.equals("world")) System.out.println("w");
break;
}
- 所有
case的hashCode()在编译期就被算好、硬编码进 class 文件 - 运行时只调一次
hashCode(),不是每个case都算一遍 - 每个分支前都插了
if (tmp.equals(...)),防哈希碰撞(比如"Aa"和"BB"的 hash 相同)
哪些写法会直接编译失败?
编译器对 case 值要求极严,不是“能运行就行”,而是“必须在编译期确定值和 hash”。
-
case sVar:变量名不行,哪怕它指向字面量,sVar不是编译期常量 ❌ -
case new String("abc"):对象创建在运行时,不是常量表达式 ❌ -
case PREFIX + "suffix":拼接含非常量,编译器无法静态推导结果 ❌ -
static final String A = "a"; case A::可以,但A必须是static final且初始化为字面量或常量表达式 ✅ -
var s = "x"; case s::var声明的局部变量,即使赋值字面量,也不被视为常量 ❌
null 和大小写问题怎么处理?
switch 对 String 没有自动判空或忽略大小写机制,这些全是业务逻辑责任。
-
null传入直接抛NullPointerException,不会进default;必须前置判断:if (s == null) { ... } else switch(s) { ... } - 大小写敏感是默认行为;需要忽略时,统一转小写再进
switch:switch (s.toLowerCase()) { case "ok": ... } - 国际化场景慎用
toLowerCase(),应使用带Locale的版本,比如s.toLowerCase(Locale.ENGLISH) - 如果分支多且逻辑重,
Map<string runnable></string>或枚举封装比switch更易维护、更安全
真正容易被忽略的是:case 少于 3 个时,javac 可能根本不用哈希表,直接退化成 if-else;而哈希分布密集(比如一堆 "cmd_XXX")会导致大量 equals() 调用,性能反而不如手写线性查找。别只看语法糖,得看字节码和实际 profile 数据。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










