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