Java switch 寫法的正確姿勢是什麼?刷新我的認知

首先我們看下阿里泰山版的Java 開發手冊的規範:

1、在一個switch塊中,每個case通過continue/break/return 等來終止,要麼 註釋說明程序將接續執行哪一個case為止;在一個switch塊內,都必須包含一個default語句並且放在最後,即使什麼代碼都沒有。

說明:注意break是退出switch語句塊,而return是退出方法體;

2、當switch括號內的變量類型為String並且此變量為外部參數時,必須先進行null判斷;

反例:

<code>

public

class

Demo

{

public

static

void

main

(

String[] args

)

{ methodDemo(

null

); }

public

static

void

methodDemo

(

String param

)

{

switch

(param){

case

"nith"

: System.

out

.println(

"it is nith!"

);

case

"null"

: System.

out

.println(

"It is null!"

);

default

: System.

out

.println(

"default"

); } } }/<code>

那麼上述的結果是什麼呢?


Java switch 寫法的正確姿勢是什麼?刷新我的認知

那為什麼是空指針呢?我們點擊switch是看不到源碼呢?那我們就從字節碼層次進行分析,為什麼是空指針?


<code>

public

class

com.wy.demo.Demo

{

public

com.wy.demo.Demo();

Code:

0:

aload_0

1:

invokespecial

4:

return

public

static

void

main(java.lang.String[]);

Code:

0:

aconst_null

1:

invokestatic

4:

return

public

static

void

methodDemo(java.lang.String);

Code:

0:

aload_0

1:

astore_1

2:

iconst_m1

3:

istore_2

4:

aload_1

5:

invokevirtual

8:

lookupswitch

{

//

2

3381615:

36

3392903:

50

default:

61

}

36:

aload_1

37:

ldc

39:

invokevirtual

42:

ifeq

61

45:

iconst_0

46:

istore_2

47:

goto

61

50:

aload_1

51:

ldc

53:

invokevirtual

56:

ifeq

61

59:

iconst_1

60:

istore_2

61:

iload_2

62:

lookupswitch

{

//

2

0:

88

1:

96

default:

104

}

88:

getstatic

91:

ldc

93:

invokevirtual

96:

getstatic

99:

ldc

101:

invokevirtual

104:

getstatic

107:

ldc

109:

invokevirtual

112:

return

}

/<code>
  • 0-3 :做了一些初始化操作,把入參param賦給局部變量表下標為2的變量
  • 4-8 : 對入參param調用了hashCode函數,得到一個整型值。因此一般而言hash都是比較離散,所以沒有選擇tableswitch 而是用lookupswitch來作為switch case的實現
  • 36 ~ 47:如果 hashCode 等於 "Java".hashCode() 會跳轉到這部分字節碼。首先把字符串進行真正意義上的 equals 比較,看是否相等,是否相等使用的是 ifeq 指令, ifeq 這個指令語義上有點繞,ifeq 的含義是ifeq 0則跳轉到對應字節碼行處,實際上是等於 false 跳轉。這裡如果相等則把 matchIndex 賦值為 0
  • 61 ~ 96:進行最後的 case 分支執行。這一段比較好理解,不再繼續做分析。

從上面代碼看,如果我們傳入的是null,null的hashcode值就會包空指針;


結合上面字節碼解讀,我們可以推演出對應的java實現代碼:

<code>    

public

void

test

(

String param

)

{ String tmpName = param;

int

matchIndex =

-1

;

switch

(tmpName.hashCode()){

case

3381615

:

if

(tmpName.

equals

(

"nith"

)) { matchIndex =

1

; }

break

;

case

3392903

:

if

(tmpName.

equals

(

"null"

)) { matchIndex =

0

; }

break

;

default

:

break

; }

switch

(matchIndex) {

case

0

: System.

out

.println(

"It is null!"

);

case

1

: System.

out

.println(

"It is null!"

);

default

: System.

out

.println(

"default"

); } }/<code>

所以我們最後總結一下:String的switch實現流程分為以下幾個步驟:

1、計算字符串的hashCode

2、使用 lookupswitch 對整型 hashCode 進行分支

3、對相同 hashCode 值的字符串進行最後的字符串匹配

4、執行 case 塊代碼


分享到:


相關文章: