為什么switch里的case沒有break不行


前言

一個小姐姐拿着一個switch的選擇題來問我。

之所以這么篤定地回答這個問題,並不是我知道其中原理,而是之前在一個群里,有人問了同類型的問題,我瞥了一眼記住了答案,所以才依葫蘆畫瓢。

小姐姐接着問我為什么,我說少個break,但凡再問一句:為什么少個break結果就不一樣,我就回答不出來了。所以,為了將尷尬扼殺於搖籃,還是研究一下break在switch的作用。

從字節碼出發

按照慣例,先寫demo表述問題。

 public static void main(String[] args) {
    int i = 0;
    switch (i) {
        case 0:
            System.out.println(0);
        case 1:
            System.out.println(1);
        case 2:
            System.out.println(2);
  }

運行代碼,結果如下:

*明明只匹配了case 0,為什么1和2也執行了? 很費解!按照慣用套路,看看字節碼能不能給個答案。

javac編譯和javap查看:

tableswitchlookupswitch都用於switch條件跳轉,前者用於case值連續,例如上面代碼中的0、1、2;后者用於case值不連續。

從字節碼可以看出:switch中的case條件和對應代碼塊是分開的。如上圖,case為0時,跳轉到標號28代碼處;為1時跳轉到標號35代碼處;為2時跳轉到標號43代碼處;default則跳轉到標號49代碼處。

這不,答案就出來了,當case 0匹配了之后,直接跳轉到標號28代碼處開始執行,輸出0,然后策馬奔騰,一路小下坡,順序執行完后面所有代碼,直到標號49 return,方法完執行完成,程序結束。

如果按照正常的思維,是不是case 0匹配之后,跳到28,執行完28、31、32輸出0之后,就應該直接跳走,直接執行49。那么,這個"跳走”用字節碼應該怎么表示?

用return?那不行,因為return會結束方法,這樣switch后代碼也無法執行。那怎么辦嘞....

關於goto

goto:無條件跳轉,goto 1表示跳轉到標號1的代碼處。

再寫代碼樣例,這次在代碼中給每個case都加上break。

  public static void main(String[] args) {
      int i = 0;
      switch (i) {
          case 0:
              System.out.println(0);
              break;
          case 10:
              System.out.println(1);
              break;
          case 2:
              System.out.println(2);
              break;
      }
      System.out.println("Hello World");
  }

重新編譯,再來看看字節碼。

如圖,與第一次的字節碼相比,在標號35、45都有了goto指令。如果case 0匹配成功,則跳到標號28執行,執行完代碼塊對應的31、32指令之后,執行35的goto指令跳轉到標號55,這樣就跳出了switch作用范圍,case 1和2也不會被執行。

等等,怎么少了一個goto,在標號55的上方應該還有一個goto才對!其實這就涉及到了編譯器優化技術,最后一個goto也是跳轉到標號55的指令,但沒有goto下一步也一樣順序執行此行指令,所以這個goto被編譯器視為無用代碼進行了消除。

switch和if區別

先用if實現上面switch邏輯。

  public static void main(String[] args) {
      int i = 0;
      if (i == 0) {
          System.out.println(0);
      } else if (i == 1) {
          System.out.println(1);
      } else if (i == 2) {
          System.out.println(2);
      }
  }

編譯成字節碼:

if_icmpne用於比較兩個int數。從字節碼也可以看出if和switch的區別:if條件和代碼塊的字節碼是順序的,switch條件和代碼塊是分開的;if自動生成goto指令,switch只有加了break才生成goto指令

結語

case中的break告訴前端編譯器:給每個case對應代碼塊的最后加上goto。這樣,執行完匹配上的代碼之后,就可以略過后面的case代碼塊了。

果然,求(xiao)知(jie)欲(jie)才是學習新知識的動力。

95后小程序員,寫的都是日常工作中的親身實踐,置身於初學者的角度從0寫到1,詳細且認真。文章會在公眾號 [入門到放棄之路] 首發,期待你的關注。

感謝每一份關注


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM