看下面代码,当default在中间时,输出是什么?
public static void main(String[] args) {
//当default在中间时,且看输出是什么?
int a = 1;
switch (a) {
case 2:
System.out.println("print 2");
case 1:
System.out.println("print 1");
default:
System.out.println("first default print");
case 3:
System.out.println("print 3");
}
是不是都被注释的内容影响到了?是的,这就是我们很容易忽略掉的点,switch...case的基本用法都忘了。。。
1.default就是如果没有符合的case就执行它,default并不是必须的.
2.case后的语句可以不用大括号.
3.switch语句的判断条件可以接受int,byte,char,short,不能接受其他类型(1.7以后支持String,以及上述四种包装类).
4.一旦case匹配,就会顺序执行后面的程序代码,而不管后面的case是否匹配,直到遇见break,利用这一特性可以让好几个case执行统一语句.
没有break时,匹配到一个之后就会将下面的所有的语句都执行!!!要根据自己的需要决定加不加break
下面是switch case底层剖析
1.首先我们知道JDK1.7及之后支持了String,也知道它是不支持null的,如下会出现NullPointException
public static void main(String[] args) {
String str = null;
switch (str) {
case "String":
System.out.println("this is String");
case "null":
System.out.println("this is null String");
case "帅":
System.out.println("帅");
default:
System.out.println("this is default");
}
}
查看class文件如下
public static void main(String[] args) {
String str = "str";
byte var3 = -1;
switch(str.hashCode()) {
case -1808118735:
if (str.equals("String")) {
var3 = 0;
}
break;
case 24069:
if (str.equals("帅")) {
var3 = 2;
}
break;
case 3392903:
if (str.equals("null")) {
var3 = 1;
}
}
switch(var3) {
case 0:
System.out.println("this is String");
case 1:
System.out.println("this is null String");
case 2:
System.out.println("帅");
default:
System.out.println("this is default");
}
}
可以看到switch case对String的支持是因为做了hashCode以及equals,所以会出现NPE;然后又赋值给var3,对var3进行新的switch case
再对其进行反编译如下:可以看到两个关键的指令lookupswitch和tableswtich
2.lookupswitch和tableswitch
lookupswitch
public static int test(int i) {
int result;
switch (i) {
case 0:
result = 0;
break;
case 2:
result = 1;
break;
case 10:
result = 4;
break;
default:
result = -1;
}
return result;
}
对应如下字节码
public static int test(int);
Code:
0: iload_0
1: lookupswitch { // 3
0: 36
2: 41
10: 46
default: 51
}
36: iconst_0
37: istore_1
38: goto 53
41: iconst_1
42: istore_1
43: goto 53
46: iconst_4
47: istore_1
48: goto 53
51: iconst_m1
52: istore_1
53: iload_1
54: ireturn
tableswitch
public static int test(int i) {
int result;
switch (i) {
case 0:
result = 0;
break;
case 2:
result = 1;
break;
case 4:
result = 4;
break;
default:
result = -1;
}
return result;
}
对应字节码
public static int test(int);
Code:
0: iload_0
1: tableswitch { // 0 to 4
0: 36
1: 51
2: 41
3: 51
4: 46
default: 51
}
36: iconst_0
37: istore_1
38: goto 53
41: iconst_1
42: istore_1
43: goto 53
46: iconst_4
47: istore_1
48: goto 53
51: iconst_m1
52: istore_1
53: iload_1
54: ireturn
首先看tableswitch和lookupswitch区别:
- tableswitch使用了一个数组,通过下标可以直接定位到要跳转的行。但是在生成字节码时,有的行可能在源码中并不存在。通过这种方式可以获得O(1)的时间复杂度。(比如case 0 2 4,对应的字节码中case成了 0 1 2 3 4)
- lookupswitch维护了一个key-value的关系,通过逐个比较索引来查找匹配的待跳转的行数。而查找最好的性能是O(log n),如二分查找。
可见,通过用冗余的机器码,tableswitch换取了更好的性能。
但是,在分支比较少的情况下,O(log n)其实并不大。n=2时,log n 约为2.8;即使n=100, log n 约为 6.6,与1仍未达到1个数量级的差距。
另外Java虚拟机的tableswitch和lookupswitch指令只支持int类型,这也是为什么8种基本类型中只能支持char、byte、short、int .
因为 double、float 都是浮点类型的,tableswitch 和 lookupswitch 指令操作不了。
因为 long 类型 64 位了,而tableswitch 和 lookupswitch 指令只能操作 32 位的 int 。这两个指令对于 long 是搞不动的。
而至于 boolean 类型,还需要我说嘛?
你拿着 boolean 类型放到 switch 表达式里面去?
你就不能写个 if(boolean) 啥的?
参考https://www.cnblogs.com/wuyuegb2312/p/11172440.html
https://blog.csdn.net/csdnnews/article/details/108722499
我们知道 switch 的表达式和 case 里面都是不支持 null 的。
不过一个问题,switch/case 里面为什么不做成支持 null 的模式?
如果表达式为 null ,我们就拿着 null 去 case 里面匹配,这样理论上做也是可以做的,但是应该也不会有人问这个问题吧?谁知道能说下?或许只是设计者认为抛出空指针这样做比静默地跳过整个 switch 语句或选择在 default 标签(如果有)里面继续执行语句要好