java switch底层只认int,string通过编译期预计算hashcode生成跳转表+运行时equals校验实现支持;long因取值范围超int且无安全截断规则被拒绝。

Java 的 switch 语句**表面上支持多种类型,但底层只认 int**。所有能进 switch 的类型,都必须能被编译器“翻译”成一个确定的、可查表的整数——这是理解支持范围和 String 原理的关键。
哪些类型能用?为什么能用?
能用的类型分三类,本质都是“可安全转为 int”:
- 基本整型及其包装类:byte、short、char、int,以及 Byte、Short、Character、Integer。byte/short/char 会自动提升为 int;包装类靠自动拆箱转成基本类型再提升。
-
枚举(enum):编译器用
ordinal()方法取其声明顺序(从 0 开始的 int),所以本质上还是跳转到一个整数索引。 -
String(JDK 7+):不是直接比内容,而是靠编译器预计算每个 case 字符串的
hashCode(),生成整数跳转表——它走的是“哈希先行 + 内容兜底”的双保险路径。
String 进 switch 的真实执行过程
你写的是:
编译器实际把它“重写”为两层逻辑:
- 第一层:调用
str.hashCode(),拿到一个 int 值,用tableswitch或lookupswitch快速跳转到可能匹配的分支块; - 第二层:在对应分支里,用
str.equals("apple")严格校验内容——这是为了防止不同字符串产生相同哈希值(哈希冲突)导致误判。
反编译后常见类似结构:
switch(str.hashCode()) {
case 96321: if (str.equals("apple")) { ... } break;
case 97314: if (str.equals("banana")) { ... } break;
}
为什么 long 不行?String 却可以?
关键不在“是不是引用类型”,而在“能不能无歧义地映射为 int”:
- long 取值范围远超 int(-2⁶³ ~ 2⁶³−1 vs -2³¹ ~ 2³¹−1),强制截断会丢失高位、引发不可预测行为,JVM 拒绝这种隐式风险;
-
String 虽是引用类型,但它的
hashCode()方法稳定返回 int,且编译期就能算出所有字面量 case 的哈希值,配合运行时equals()校验,既高效又安全。
使用 String switch 的注意事项
看似方便,但几个坑容易忽略:
-
str 不能为 null:调用
null.hashCode()直接抛NullPointerException,编译不报错,运行必崩; -
case 字符串必须是编译期常量:比如
"hello"可以,new String("hello")或变量不行; -
哈希冲突虽少见,但存在:如
"Aa"和"BB"的 hashCode 都是 2112;编译器会把它们分到同一跳转入口,靠后续equals()区分——性能略降,但逻辑不破。











