版本:jdk11,其他选项默认
1. switch基本语法
switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
switch(expression){
case value :
//语句
break; //可选
case value :
//语句
break; //可选
//你可以有任意数量的case语句
default : //可选
//语句
}
switch case 语句有如下规则:
- switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
- switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
- case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
- 当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
- 当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
- switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。
2. case自动类型转换
byte
、short
、char
会自动转换成int类型,然后进行比较;String
类型会调用hashcode()
方法,用int
类型返回值进行比较,然后使用equals()
进行进一步判断;enum
类型,会调用对象的ordinal()
方法,返回其对应的int
类型下标;
所有的case最终都会被转换成int类型,然后进行编译。
3. case匹配方式 lookupswitch & tableswitch
tableswitch:带有标签的表格。
lookupswitch:带有key索引和标签的表格。
当case比较紧凑时且数值接近0时,通常使用tableswitch
,直接使用下标进行索引,例如代码下文代码示例中的chooseNear()
方法,可以看到多出了几个选项,指向了default
选项L4
。效率为O(1)。
当case比较分散时且数值与0距离较远,通常使用lookupswitch
,其中的数值进行了排序,采用二分法进行查找,效率为O(log n)。
4. 代码示例
代码:
public class SmallIntDemo {
public static void main(String[] args) {
System.out.println(chooseFar(1));
System.out.println(chooseFar(1));
System.out.println(charNear('a'));
System.out.println(stringMethod("hello"));
System.out.println(enumMethod(CharEnum.a));
}
enum CharEnum {
a,
b,
c
}
static int chooseNear(int i) {
switch (i) {
case 0:
return 0;
case -1:
return 0;
case 1:
return 1;
case 2:
return 2;
case 8:
return 8;
default:
return -1;
}
}
static int chooseFar(int i) {
switch (i) {
case -100:
return -1;
case 0:
return 0;
case 100:
return 1;
default:
return -1;
}
}
static int charNear(char c) {
switch (c) {
case 'a':
return 0;
case 'b':
return 0;
case 'c':
return 1;
default:
return -1;
}
}
static int stringMethod(String str) {
switch (str) {
case "ok":
return 0;
case "fine":
return 0;
case "hello":
return 1;
default:
return -1;
}
}
static char enumMethod(CharEnum ce) {
switch (ce) {
case a:
return 'a';
case b:
return 'b';
case c:
return 'c';
default:
return ' ';
}
}
}
编译结果:
// access flags 0x8
static chooseNear(I)I
L0
LINENUMBER 30 L0
ILOAD 0
TABLESWITCH
-1: L1
0: L2
1: L3
2: L4
3: L5
4: L5
5: L5
6: L5
7: L5
8: L6
default: L5
L2
LINENUMBER 32 L2
FRAME SAME
ICONST_0
IRETURN
L1
LINENUMBER 34 L1
FRAME SAME
ICONST_0
IRETURN
L3
LINENUMBER 36 L3
FRAME SAME
ICONST_1
IRETURN
L4
LINENUMBER 38 L4
FRAME SAME
ICONST_2
IRETURN
L6
LINENUMBER 40 L6
FRAME SAME
BIPUSH 8
IRETURN
L5
LINENUMBER 42 L5
FRAME SAME
ICONST_M1
IRETURN
L7
LOCALVARIABLE i I L0 L7 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x8
static chooseFar(I)I
L0
LINENUMBER 47 L0
ILOAD 0
LOOKUPSWITCH
-100: L1
0: L2
100: L3
default: L4
L1
LINENUMBER 49 L1
FRAME SAME
ICONST_M1
IRETURN
L2
LINENUMBER 51 L2
FRAME SAME
ICONST_0
IRETURN
L3
LINENUMBER 53 L3
FRAME SAME
ICONST_1
IRETURN
L4
LINENUMBER 55 L4
FRAME SAME
ICONST_M1
IRETURN
L5
LOCALVARIABLE i I L0 L5 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x8
static charNear(C)I
L0
LINENUMBER 60 L0
ILOAD 0
TABLESWITCH
97: L1
98: L2
99: L3
default: L4
L1
LINENUMBER 62 L1
FRAME SAME
ICONST_0
IRETURN
L2
LINENUMBER 64 L2
FRAME SAME
ICONST_0
IRETURN
L3
LINENUMBER 66 L3
FRAME SAME
ICONST_1
IRETURN
L4
LINENUMBER 68 L4
FRAME SAME
ICONST_M1
IRETURN
L5
LOCALVARIABLE c C L0 L5 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x8
static stringMethod(Ljava/lang/String;)I
L0
LINENUMBER 73 L0
ALOAD 0
ASTORE 1
ICONST_M1
ISTORE 2
ALOAD 1
INVOKEVIRTUAL java/lang/String.hashCode ()I
LOOKUPSWITCH
3548: L1
3143098: L2
99162322: L3
default: L4
L1
FRAME APPEND [java/lang/String I]
ALOAD 1
LDC "ok"
INVOKEVIRTUAL java/lang/String.equals (Ljava/lang/Object;)Z
IFEQ L4
ICONST_0
ISTORE 2
GOTO L4
L2
FRAME SAME
ALOAD 1
LDC "fine"
INVOKEVIRTUAL java/lang/String.equals (Ljava/lang/Object;)Z
IFEQ L4
ICONST_1
ISTORE 2
GOTO L4
L3
FRAME SAME
ALOAD 1
LDC "hello"
INVOKEVIRTUAL java/lang/String.equals (Ljava/lang/Object;)Z
IFEQ L4
ICONST_2
ISTORE 2
L4
FRAME SAME
ILOAD 2
TABLESWITCH
0: L5
1: L6
2: L7
default: L8
L5
LINENUMBER 75 L5
FRAME SAME
ICONST_0
IRETURN
L6
LINENUMBER 77 L6
FRAME SAME
ICONST_1
IRETURN
L7
LINENUMBER 79 L7
FRAME SAME
ICONST_2
IRETURN
L8
LINENUMBER 81 L8
FRAME SAME
ICONST_M1
IRETURN
L9
LOCALVARIABLE str Ljava/lang/String; L0 L9 0
MAXSTACK = 2
MAXLOCALS = 3
// access flags 0x8
static enumMethod(Lswitch_case/SmallIntDemo$CharEnum;)C
L0
LINENUMBER 86 L0
GETSTATIC switch_case/SmallIntDemo$1.$SwitchMap$switch_case$SmallIntDemo$CharEnum : [I
ALOAD 0
INVOKEVIRTUAL switch_case/SmallIntDemo$CharEnum.ordinal ()I
IALOAD
TABLESWITCH
1: L1
2: L2
3: L3
default: L4
L1
LINENUMBER 88 L1
FRAME SAME
BIPUSH 97
IRETURN
L2
LINENUMBER 90 L2
FRAME SAME
BIPUSH 98
IRETURN
L3
LINENUMBER 92 L3
FRAME SAME
BIPUSH 99
IRETURN
L4
LINENUMBER 94 L4
FRAME SAME
BIPUSH 32
IRETURN
L5
LOCALVARIABLE ce Lswitch_case/SmallIntDemo$CharEnum; L0 L5 0
MAXSTACK = 2
MAXLOCALS = 1
}
4. 参考文档
[2] Chapter 3. Compiling for the Java Virtual Machine
[3] stackoverflow: Difference between JVM's LookupSwitch and TableSwitch?