在 switch-case 語句的條件判斷中,或許使用整形或者列舉更好,但由於種種歷史原因,專案中已大量使用字串的情況下,只得硬著皮頭往前衝了。對 switch 支援 String 的實現原理感興趣的原因,是在跟組員探討線上的一個空指標異常來的,以前根本沒意識到小小的switch 還有這樣的玩法。
要對 switch 的原理追根溯源,我們來寫一段簡單的 switch 程式碼,反編譯來看看位元組碼層是什麼效果。
public class Testk {
public static void main(String[] args) {
String key = null;
switch (key) {
case "java":
System.out.println("caught java");
break;
case "android":
System.out.println("caught android");
break;
}
}
}
這裡我們定義引數key,根據 key 值跳轉不同的 case 邏輯。
正常情況下,使用 “javac <.java> ”生成.class 檔案,使用“javap -verbose <.class>”即可得到位元組碼,但由於javap得到的位元組碼結構難以理解,這裡我們使用 JD-Gui 工具來檢視。
Mac 下安裝JD-Gui工具
brew cask install jd-gui
把*.class 檔案拖入開啟的 JD-Gui視窗,即可得到如下結果:
import java.io.PrintStream;
public class Testk
{
public static void main(String[] paramArrayOfString)
{
Object localObject1 = null;
Object localObject2 = localObject1;int i = -1;
switch (((String)localObject2).hashCode())
{
case 3254818:
if (((String)localObject2).equals("java")) {
i = 0;
}
break;
case -861391249:
if (((String)localObject2).equals("android")) {
i = 1;
}
break;
}
switch (i)
{
case 0:
System.out.println("caught java");
break;
case 1:
System.out.println("caught android");
}
}
}
通過編譯後的程式碼,我們知道 switch 處理字串是先獲取hashCode ->equals()來實現的。
看到這裡,我們明白文首的空指標是怎麼來的了,編譯器針對 switch 的 String 做編譯處理時, 需要針對 key 做做非空校驗
另外,這裡先基於 hashCode()再 equals()方法進行安全檢查是有必要的,用來避免 hash 碰撞。
網上很多人都不建議使用字串,給出的理由多半是String 的大小寫使得程式碼更脆弱。在我看來,程式碼的脆弱多數是研發人員的程式碼風格不規範導致。
就拿上面的程式碼片段來說,改成全域性定義變數即可解決大小寫敏感問題。
public class Testk {
private static final String KEY_JAVA=“java”;
private static final String KEY_ANDROID=“android”;
public static void main(String[] args) {
String key = null;
switch (key) {
case KEY_JAVA:
System.out.println("caught java");
break;
case KEY_ANDROID:
System.out.println("caught java");
break;
}
}
}