java基础(六) switch语句的深入解析
2018-03-04 01:25
579 查看
引言
switch 语句是非常的基础的知识,掌握起来也不难掌握,语法比较简单。但大部分人基本是知其然,不知其所以然。譬如 早期JDK只允许switch的表达式的值 int及int类型以下的基本类型,后期的JDK却允许匹配比较 字符串、枚举类型,这是怎么做到的呢?原理是什么?本文将深入去探索。一、switch 介绍
switch 语法格式:switch (表达式) { case 常量表达式或枚举常量: 语句; break; case 常量表达式或枚举常量: 语句; break; ...... default: 语句; break; }
switch 匹配的表达式可以是:
byte、short、char、int类型及 这4种类型的包装类型;
枚举类型;
String 类型;
case 匹配的表达式可以是:
常量表达式;
枚举常量;
注意一点: case提供了switch表达式的入口地址,一旦switch表达式与某个case分支匹配,则从该分支的语句开始执行,一直执行下去,即其后的所有case分支的语句也会被执行,直到遇到break语句。
看个例子体会一下:
public static void main(String[] args) { String s = "a"; switch (s) { case "a": //a分支 System.out.println("匹配成功1"); case "b": //b分支 System.out.println("匹配成功2"); case "c": //c分支 System.out.println("匹配成功3"); break; case "d": //d分支 System.out.println("匹配成功4"); break; default: break; } }
运行结果:
匹配成功1
匹配成功2
匹配成功3
switch成功匹配了a分支,但a、b分支都没有 break 语句,所以一直执行a分支后的所有语句,直到遇到c分支的break语句才终止。
二、编译器对 switch 表达式的各种类型的处理
尽管 switch 支持的类型扩充了几个,但其实在底层中,swtich 只能支持4种基本类型,其他几个类型是通过一些方式来间接处理的,下面便是讲解编译器对扩充类型的处理。1、对包装类的处理
对包装类的处理是最简单的 —— 拆箱。看下面的例子,switch 比较的是包装类 Byte 。Byte b = 2; switch (b) { case 1: System.out.println("匹配成功"); break; case 2: System.out.println("匹配成功"); break; }
用jad反编译一下这段代码,得到的代码如下:
Byte b = Byte.valueOf((byte)2); switch(b.byteValue()) { case 1: // '\001' System.out.println("\u5339\u914D\u6210\u529F"); break; case 2: // '\002' System.out.println("\u5339\u914D\u6210\u529F"); break; }
反编译的代码很简单,底层的switch比较的是Byte通过(拆箱)方法byteValue()得到的byte值。顺便说一下,这段反编译代码不仅揭开了 拆箱 的解析原理,也展示了 装箱 的解析原理(第一句代码);
2. 枚举类型
为了简单起见,直接采用JDK提供的枚举类型的线程状态类Thread.state类。
Thread.State state = Thread.State.RUNNABLE; switch (state) { case NEW: System.out.println("线程处于创建状态"); break; case RUNNABLE: System.out.println("线程处于可运行状态"); break; case TERMINATED: System.out.println("线程结束"); break; default: break; }
反编译代码:
Sex sex = Sex.MALE; switch($SWITCH_TABLE$Test_2018_1_14$Sex()[sex.ordinal()]) { case 1: // '\001' System.out.println("sex:male"); break; case 2: // '\002' System.out.println("sex:female"); break; }
从编译代码中发现,编译器对于枚举类型的处理,是通过创建一个辅助数组来处理,这个数组是通过一个
$SWITCH_TABLE$java$lang$Thread$State()方法创建的,数组是一个int[]类型数组,数组很简单,在每个枚举常量的序号所对应的数组下标位置的赋一个值,按序号大小赋值,从1开始递增。 其代码如下:
//int 数组 private static int $SWITCH_TABLE$java$lang$Thread$State[]; //创建数组的方法 static int[] $SWITCH_TABLE$java$lang$Thread$State() { $SWITCH_TABLE$java$lang$Thread$State; if($SWITCH_TABLE$java$lang$Thread$State == null) goto _L2; else goto _L1 _L1: return; _L2: JVM INSTR pop ; int ai[] = new int[Thread.State.values().length]; try { ai[Thread.State.BLOCKED.ordinal()] = 3; } catch(NoSuchFieldError _ex) { } try { ai[Thread.State.NEW.ordinal()] = 1; } catch(NoSuchFieldError _ex) { } try { ai[Thread.State.RUNNABLE.ordinal()] = 2; } catch(NoSuchFieldError _ex) { } try { ai[Thread.State.TERMINATED.ordinal()] = 6; } catch(NoSuchFieldError _ex) { } try { ai[Thread.State.TIMED_WAITING.ordinal()] = 5; } catch(NoSuchFieldError _ex) { } try { ai[Thread.State.WAITING.ordinal()] = 4; } catch(NoSuchFieldError _ex) { } return $SWITCH_TABLE$java$lang$Thread$State = ai; } }
3、 对String类型的处理
依旧是先看个例子,再查看这个例子反编译代码,了解编译器的是如何解析的。public static void main(String[] args) { String s = "China"; switch (s) { case "America": System.out.println("匹配到美国"); break; case "China": System.out.println("匹配到中国"); break; case "Japan": System.out.println("匹配到日本"); default: break; } }
反编译得到的代码:
public static void main(String args[]) { String s = "China"; String s1; switch((s1 = s).hashCode()) { default: break; case 65078583: if(s1.equals("China")) System.out.println("\u5339\u914D\u5230\u4E2D\u56FD"); break; case 71341030: if(s1.equals("Japan")) System.out.println("\u5339\u914D\u5230\u65E5\u672C"); break; case 775550446: if(s1.equals("America")) System.out.println("\u5339\u914D\u5230\u7F8E\u56FD"); break; } }
从反编译的代码可以看出,switch 的String变量、case 的String常量都变成对应的字符串的 hash 值。也就是说,switch仍然没有超出它的限制,只是通过使用 String对象的hash值来进行匹配比较,从而支持 String 类型。
总结:
底层的switc只能处理4个基本类型的值。其他三种类型需要通过其他方式间接处理,即转成基本类型来处理。编译器对包装类的处理是通过 拆箱。
对枚举类型的处理,是通过枚举常量的序号及一个数组。
对字符串String的处理,是通过 String 的hash值。
作者:jinggod
出处:http://www.cnblogs.com/jinggod/p/8425260.html
相关文章推荐
- 黑马程序员 java基础--》 if语句与switch语句 小结
- Java 基础源码 switch语句判断指定月份属于一年中的哪个季度
- JAVA基础 (三)反射 深入解析反射机制
- JAVA基础 (二)反射 深入解析反射机制
- Java基础(极客)——02、Java语言Switch语句详解
- 黑马程序员 Java基础<二>--->数据类型,数组,switch语句
- 面试题解析:1 Java中switch语句可以作用在enum上的测试
- Java基础知识强化19:Java中switch分支语句
- JAVA_SE基础——46.引用数据类型变量.值交换[独家深入解析]
- 磨刀不误砍材工 - Java的基础语言要素(语句-深入理解)
- Java基础之switch语句
- java基础知识:数据类型,switch语句,分支结构
- 黑马程序员——java基础知识篇——>if语句、switch语句
- Java基础深入解析——类与对象
- JAVA基础 (二)反射 深入解析反射机制
- Java基础——switch、循环语句、控制跳转语句
- 黑马程序员——java第三天:基础语法(三元运算符,while、switch、for语句)
- Java基础深入解析——类与对象
- Java基础--->08.判断语句(if、else if、switch、while、do while)。
- JAVA基础 (三)反射 深入解析反射机制