Java if-else 之不加大括號可能會有什么后果


起因

最近在刷題,有一簡單題 20. 有效的括號
我的作答如下,感覺可以順利通過,卻怎么也通不過:

public boolean isValid(String s) {
    if (s.length() % 2 != 0) return false;
    char[][] blacks = new char[][]{{'(', ')'}, {'[', ']'}, {'{', '}'}};
    Stack<Character> stack = new Stack<>();
    for (char c : s.toCharArray()) {
        if (c == blacks[0][0]) stack.push(blacks[0][1]);
        else if (c == blacks[1][0]) stack.push(blacks[1][1]);
        else if (c == blacks[2][0]) stack.push(blacks[2][1]);
        else if (c == blacks[0][1]) if (stack.isEmpty() || stack.pop() != blacks[0][1]) return false;
        else if (c == blacks[1][1]) if (stack.isEmpty() || stack.pop() != blacks[1][1]) return false;
        else if (c == blacks[2][1]) if (stack.isEmpty() || stack.pop() != blacks[2][1]) return false;
    }
    return stack.isEmpty();
}

輸入是:
()[]{}
最終輸出false

打印棧中剩余元素:
],}

怎么會這樣呢?這和平時的直覺完全不一樣啊。

最開始猜測可能是包裝類對象地址不同的問題,但是一看明顯又不是,一個是包裝類,一個是基本數據類型,會自動拆裝箱,不是會是這個問題。

於是猜測可能是else if 后面沒有加大括號的原因,加上大括號順利通過。

之前以為else if 只要是一句話就行,以;算是結束,但是顯然,就這題而言並不是這樣的。

深入探究

為了深入探究為什么會這樣,查看java字節碼如下,截取第一個else if對應的字節碼如下:

   L10
    LINENUMBER 80 L10  // 第80行
   FRAME SAME
    ILOAD 7 
    ALOAD 2 
    ICONST_0 
    AALOAD   
    ICONST_1 
    CALOAD   
    IF_ICMPNE L8  // 比較棧頂兩個int類型數值大小,當前者不等於后者時跳轉到L8,L8是for循環開始的地方
    ALOAD 3
    INVOKEVIRTUAL java/util/Stack.isEmpty ()Z
    IFNE L11  // 如果棧空,到L11,L11是返回false
    ALOAD 3
    INVOKEVIRTUAL java/util/Stack.pop ()Ljava/lang/Object;
    CHECKCAST java/lang/Character
    ALOAD 2
    ICONST_0
    AALOAD
    ICONST_1
    CALOAD
    INVOKESTATIC java/lang/Character.valueOf (C)Ljava/lang/Character;
    INVOKEVIRTUAL java/lang/Character.equals (Ljava/lang/Object;)Z  // true 1, false,0
    IFNE L12 // 當棧頂int類型數值不等於0時跳轉 L12,即, 兩個元素相等,跳轉下一個if條件
   L11  // 這段邏輯就是 return false;
   FRAME SAME
    ICONST_0
    IRETURN

從字節碼可以看出來,第一個else if其邏輯會變成這樣:

  1. 第一個條件判斷:如果不相等,直接開始下一輪循環;如果相等,那么會進入if判斷:
  2. if判斷:如果棧空返回false,如果棧不空,那么判斷棧頂元素和當前元素是否相等:
  3. 如果相等進入下一個條件判斷,如果不相等,那么返回false

這邏輯很詭異:
對於第1條,我們希望,第一個條件判斷是如果不相等進入到下一個條件判斷,但是這里直接進入下一輪循環了,后邊的代碼直接跳過了...
對於第3條,如果元素相等,那么應該是進入下一輪循環,這里竟然是走到下一個條件判斷...

所以根據這樣的邏輯,輸入:
()[]{}
那么一共六輪循環:

  1. ) 入棧
  2. 第一個elif 相等,進入if,棧不空,棧頂元素()順利出棧)和當前元素相等,那么進入下一輪條件判斷,第二個else-if判斷因為不相等直接到下一輪循環
  3. ] 入棧
  4. 第一個elif 不相等,直接進入下一輪循環
  5. } 入棧
  6. 第一個elif 不相等,直接進入下一輪循環

所以最終棧內只剩下元素:
]}

經過debug,和梳理的邏輯一致。

總結

平時在工作中寫代碼時,我都會加上大括號,因為這是java規范所要求的,一是便於閱讀,而是后面新增代碼時,不容易出錯。

但是刷題時,就是怎么看起來簡潔怎么來,沒想到if-else不用大括號還會遇到這樣的坑...

最后:如果使用到if-else的條件判斷,最好還是加上大括號,特別是后邊跟着比較復雜的表達式時。

參考資料

https://segmentfault.com/a/1190000037628881


免責聲明!

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



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