switch
语句是一个很容易忽略的语法点,在表达式支持的类型上也犯过很多错,今天就来整理一下
switch语句基本定义:
switch (表达式){
case 值1:
语句体1;
break;
case 值2:
语句体2;
break;
...
default:
语句体n+1;
break;
}
break在switch语句中的作用
关于break
在switch
语句的使用可以参考这篇博客https://tech.souyunku.com/EthanWong/p/13190595.html
表达式的取值
表达式的取值类型
1、 在JDK6及以前,表达式只能是一个常量表达式或枚举常量。所以表达式的取值可以是:
* `byte`、`short`、`int`、`char`四种基本类型,以及其包装类型
* `Enum`枚举类型
2、 在JDK7以后新增支持String类型
编译器对表达式取值的处理
虽然随着JDK版本迭代,支持的新类型越来越多,但是在编译的字节码层次,switch
语句还是只能支持基本的四种类型。
1、 基本类型的处理
**`int`数据类型**
int a = 2;
switch (a)
{
case 1:
System.out.println("first");
break;
case 2:
System.out.println("second");
break;
case 3:
System.out.println("second");
break;
default:
System.out.println("null");
break;
}
反编译后的代码
byte byte0 = 2;
switch (byte0)
{
case 1: // '\001'
System.out.println("first");
break;
case 2: // '\002'
System.out.println("second");
break;
case 3: // '\003'
System.out.println("second");
break;
default:
System.out.println("null");
break;
}
其实从这里就可以看出,正是因为`int`和`byte`、`char`、`short`之间可以隐式转换。所以可以直接支持其对应的四种包装类型
**`char`类型的处理**
char c = '2';
switch (c)
{
case '1':
System.out.println("first");
break;
case '2':
System.out.println("second");
break;
case '3':
System.out.println("second");
break;
default:
System.out.println("null");
break;
}
反编译后的代码:
byte byte0 = 50;
switch (byte0)
{
case 49: // '1'
System.out.println("first");
break;
case 50: // '2'
System.out.println("second");
break;
case 51: // '3'
System.out.println("second");
break;
default:
System.out.println("null");
break;
}
从代码来看,底层是通过比较字符的ASCII码来进行判断的。
2、 包装类型的处理
Integer I = 4;
switch (I)
{
case 1:
System.out.println("first");
break;
case 2:
System.out.println("second");
break;
case 3:
System.out.println("second");
break;
default:
System.out.println("null");
break;
}
经过反编译后的代码是
Integer integer = Integer.valueOf(4);
switch (integer.intValue())
{
case 1: // '\001'
System.out.println("first");
break;
case 2: // '\002'
System.out.println("second");
break;
case 3: // '\003'
System.out.println("second");
break;
default:
System.out.println("null");
break;
}
从反编译的代码中可以看出,`Integer`装箱的时候自动调用`Integer`的`valueof(int)`方法,拆箱的时候是自动调用`Integer`的`intValue`方法。对应到其他包装类型和Integer也类似。
3、 枚举类型的处理
public enum ColorEnum{
RED,GREEN,YELLOW;
}
public class EnumTest{
public static void main(String args[]){
ColorEnum color = ColorEnum.YELLOW;
switch(color){
case RED:
System.out.println("Stop");
break;
case GREEN:
System.out.println("Pass");
break;
case YELLOW:
System.out.println("Wait");
break;
default:
System.out.println("null");
break;
}
}
}
反编译:
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
// Source File Name: EnumTest.java
import java.io.PrintStream;
public class EnumTest
{
public EnumTest()
{
}
public static void main(String args[])
{
ColorEnum colorenum = ColorEnum.YELLOW;
static class 1
{
static final int $SwitchMap$ColorEnum[];
//自动生成int数组,通过编号来表示枚举
static
{
$SwitchMap$ColorEnum = new int[ColorEnum.values().length];
try
{
$SwitchMap$ColorEnum[ColorEnum.RED.ordinal()] = 1;
}
catch (NoSuchFieldError nosuchfielderror) { }
try
{
$SwitchMap$ColorEnum[ColorEnum.GREEN.ordinal()] = 2;
}
catch (NoSuchFieldError nosuchfielderror1) { }
try
{
$SwitchMap$ColorEnum[ColorEnum.YELLOW.ordinal()] = 3;
}
catch (NoSuchFieldError nosuchfielderror2) { }
}
}
switch (1..SwitchMap.ColorEnum[colorenum.ordinal()])
{
case 1: // '\001'
System.out.println("Stop");
break;
case 2: // '\002'
System.out.println("Pass");
break;
case 3: // '\003'
System.out.println("Wait");
break;
default:
System.out.println("null");
break;
}
}
}
从反编译的代码可以看出,底层通过创建`$SwitchMap$ColorEnum[]`的`int`数组,并通过数组的编号来表示枚举。
4、 String类型的处理
public class StringTest{
public static void main (String args[]){
String s = "RED";
switch(s){
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
case YELLOW:
System.out.println("黄色");
break;
default:
System.out.println("null");
break;
}
}
}
反编译后:
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
// Source File Name: StringTest.java
import java.io.PrintStream;
public class StringTest
{
public StringTest()
{
}
public static void main(String args[])
{
String s = "RED";
String s1 = s;//创建string对象
byte byte0 = -1;
switch (s1.hashCode())
{
case 81009: //string常量用hash值表示
if (s1.equals("RED")) //避免hash碰撞,用equals辅助判断
byte0 = 0;
break;
case 68081379:
if (s1.equals("GREEN"))
byte0 = 1;
break;
case -1680910220:
if (s1.equals("YELLOW"))
byte0 = 2;
break;
}
switch (byte0)
{
case 0: // '\0'
System.out.println("红色");
break;
case 1: // '\001'
System.out.println("绿色");
break;
case 2: // '\002'
System.out.println("黄色");
break;
default:
System.out.println("null");
break;
}
}
}
从代码中可以看出,在对`String`类型的处理中,是通过对常量的hash值和equals方法来判断比较。