1. 程式人生 > >Java 基礎語法 --- Switch

Java 基礎語法 --- Switch

0x01 關鍵字Switch

在初學程式語言的時候,從C語言開始學起,所有的語言基本都會講到switch關鍵字。他是一個用來進行分支判斷的關鍵字,和if的作用類似,但是很多時候,使用switch會更加的優雅並且更加的容易擴充套件。但是,我依稀的記得,switch關鍵字並不支援String型別(有待查驗)。

但是,不知道從JDK的哪個版本開始,switch關鍵字開始支援String了。很神奇。

0x02 Switch 基本實現

先來看一段簡單的程式碼:

class TestSwitchInteger {
	public void methodA(int params) {
		switch (params)
{ case 1: System.out.println("This is number 1."); break; case 2: System.out.println("This is number 2."); break; default: System.out.println("others number."); break; } } }

程式碼很清晰,methodA中,根據params的值來進行輸出。我們看一下編譯過後的程式碼是什麼樣子:

1: lookupswitch  { // 2
               1: 28
               2: 39
         default: 50
    }
28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 39: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 50: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;

這個地方只列出了關鍵輸出,詳細內容請自行檢視,命令如下:

# javap -v TestSwitchInteger

可以看到,程式碼被翻譯成了lookupswitch, 當輸入引數為 1

時,執行第28行的輸出。

接下來,我們看看String是怎麼實現的,先看原始碼:

public void methodA(String params) {
    switch (params) {
        case "a":
            System.out.println("a");
            break;
        case "b":
            System.out.println("b");
            break;
        default:
            System.out.println("others");
            break;
    }
}

接下來,看看編譯過後的結構:

5: invokevirtual #2         // Method java/lang/String.hashCode:()I
8: lookupswitch  { // 2
              97: 36
              98: 50
         default: 61
    }
36: aload_2
37: ldc           #3        // String a
39: invokevirtual #4        // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq          61
45: iconst_0
46: istore_3
47: goto          61
50: aload_2
51: ldc           #5        // String b
53: invokevirtual #4        // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq          61
59: iconst_1
60: istore_3
61: iload_3
62: lookupswitch  { // 2
               0: 88
               1: 99
         default: 110
    }
88: getstatic     #6         // Field java/lang/System.out:Ljava/io/PrintStream;
91: ldc           #3         // String a
93: invokevirtual #7         // Method java/io/PrintStream.println:(Ljava/lang/String;)V
96: goto          118
99: getstatic     #6         // Field java/lang/System.out:Ljava/io/PrintStream;
102: ldc           #5        // String b
104: invokevirtual #7        // Method java/io/PrintStream.println:(Ljava/lang/String;)V
107: goto          118
110: getstatic     #6        // Field java/lang/System.out:Ljava/io/PrintStream;
113: ldc           #8        // String others
115: invokevirtual #7        // Method java/io/PrintStream.println:(Ljava/lang/String;)V

可以看到,首先會執行 params.hashCode()拿到一個int值,然後去執行 int 的 switch方法,其中 97, 98 分別為a, b的hashCode,然後會分別產生 0, 1 兩個基礎值,然後在執行一次 switch方法。翻譯成Java程式碼,就是如下情況:

public void methodA(String params) {
	int a = -1;
	switch (params.hashCode()) {
		case "a".hashCode():
			a = 0; 
			break;
			case "b".hashCode():
			a = 1;
		default:
			break;
	}
	switch (a) {
		case 0:
			System.out.println("a");
			break;
		case 1:
			System.out.println("b");				
			break;
		default:
			System.out.println("others");
			break;
	}
}

0x03 思考

有些邏輯在語言層面不支援,我們可以從其它角度來考慮,說不定就能找到解決方案,就比如說上面這個,編譯的時候生成特殊的程式碼以完成對應的邏輯。在java中,還有一個和這個類似,那就是內部類呼叫外部類的private變數。有興趣的同學可以去研究一下。

在Android中也有一些庫使用了類似的方式,比如說 Butterknife, Dagger 2等。