switch
語句是一個很容易忽略的語法點,在表達式支持的類型上也犯過很多錯,今天就來整理一下
switch語句基本定義:
switch (表達式){
case 值1:
語句體1;
break;
case 值2:
語句體2;
break;
...
default:
語句體n+1;
break;
}
break在switch語句中的作用
關於break
在switch
語句的使用可以參考這篇博客https://www.cnblogs.com/EthanWong/p/13190595.html
表達式的取值
表達式的取值類型
- 在JDK6及以前,表達式只能是一個常量表達式或枚舉常量。所以表達式的取值可以是:
byte
、short
、int
、char
四種基本類型,以及其包裝類型Enum
枚舉類型
- 在JDK7以后新增支持String類型
編譯器對表達式取值的處理
雖然隨着JDK版本迭代,支持的新類型越來越多,但是在編譯的字節碼層次,switch
語句還是只能支持基本的四種類型。
-
基本類型的處理
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碼來進行判斷的。
-
包裝類型的處理
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也類似。 -
枚舉類型的處理
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
數組,並通過數組的編號來表示枚舉。 -
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方法來判斷比較。