Java 中的逻辑运算顺序令人困惑

Order of Operations with Logic in Java Confused

我有一个来自测验的操作顺序问题,解释并不是很有帮助。这是代码:

package com.udayan.oca;

public class Test {
     public static void main(String [] args) {
         int a = 2;
         boolean res = false;
         res = a++ == 2 || --a == 2 && --a == 2;
         System.out.println(a);
     }
}

它说它打印了 3 它确实如此,因为我测试了它但我不明白如何。这是他们的解释:

a++ == 2 || --a == 2 && --a == 2;

[给出的表达式]。 (a++) == 2 || --a == 2 && --a == 2;

[Postfix 的优先级高于其他运算符]。

(a++) == 2 || (--a) == 2 && (--a) == 2;

[后缀之后,前缀优先]。

((a++) == 2) || ((--a) == 2) && ((--a) == 2);

[== 的优先级高于 && 和 ||]。

((a++) == 2) || (((--a) == 2) && ((--a) == 2));

[&& 的优先级高于 ||]。

让我们开始解决它:((a++) == 2) || (((--a) == 2) && ((--a) == 2));

[a=2, res=假].

(2 == 2) || (((--a) == 2) && ((--a) == 2));

[a=3, res=假]。 true || (((--a) == 2) && ((--a) == 2));

[a=3, res=假].

||是短路运算符,因此无需计算右侧的表达式。

res 为真,a 为 3。

是的,顺便说一下短路,所以不需要解释。

但是我的想法是:

res = a++ == 2 || --a == 2 && --a == 2 ->
(((a++) == 2) || (((--a) == 2) && ((--a) == 2))) [a = 2]

(((a++) == 2) || ((**1** == 2) && ((--a) == 2))) [a = 1]

(((a++) == 2) || (**false** && (**0** == 2))) [a = 1] //short-circuits

(((a++) == 2) || **false**) [a = 1] //short circuits

(**false**) [a = 1]

???? 另一点是答案键说先做 a++ 然后 ||下一个。 a++ 是的,这是有道理的。但我认为&&在||之前。

res = a++ == 2 || --a == 2 && --a == 2 (res is true)

1. a++ (post-increment, no) -> a = a + 1 -> it's still 2 -> when true -> it becomes 3
2. --a (pre-increment, right to left) -> a - 1 = a -> 1
3. --a (pre-increment, right to left) -> a - 1 = a -> 0 (its because of logical and, it never execute this part) 
4. == (equality, left to right) -> 2 == 2 || 1 == 2 && 0 == 2 -> true || false && false
5. && (logical and, left to right) -> false -> no more steps 
6. || (logical or, left to right) -> true -> go to 1.

// so take 3
// moral of the story is always use paranthesis
// op is correct for short-circuit 

最后when(((a++) == 2) || false) [a = 1] 然后作为 || 完成优先级低于 ++ 的运算符,所以这里 a 将变为 3 .. 然后它将打印 a=3 虽然它是一个短路运算符,但它仍然必须首先执行 ++ 运算符。

来自Java Language Specification,

The conditional-or operator || operator is like | (§15.22.2), but evaluates its right-hand operand only if the value of its left-hand operand is false.

所以,这比您想象的要简单。 res = a++ == 2 || --a == 2 && --a == 2; 的计算方式如下:

res = ((a++ == 2) || ((--a == 2) && (--a == 2)));

a++ == 2? Post-increment表示a读为2。然后评估该表达式。 2 == 2,这是真的。短路意味着表达式的其余部分 never 计算。

所以,基本上上面所有的代码都是res = a++ == 2;

我做了一个简单的程序来测试这个:

public class TestSOCode {
    public static void main(String [] args) {
        test1();
    }

    private static void test1(){
        int a = 2;
        boolean res = false;
        //res = a++ == 2 || --a == 2 && --a == 2;
        res = expression(a++, "One") || expression(--a, "Two") && expression(--a, "Three");
        System.out.println(res +" "+ a);
    }

    private static boolean expression(int i, String s){
        System.out.println(s+ " called with "+ i);
        return i == 2;
    }
}

这给出了结果

One called with 2
true 3

更新: 经过一些讨论和研究,我认为在逻辑运算符方面,人们对优先级和执行顺序之间的区别存在误解。

res = a++ == 2 || --a == 2 && --a == 2;

上述语句的优先级在计算之前计算出来。我不会讨论其他优先规则,因为它会使这个答案复杂化,所以我会简化它:

res = x || y && z;

&& 优先,所以表达式像这样组合在一起:

res = x || (y && z);

正如我们所见,&& 优先,所以它左右的表达式被组合在一起,然后 || 被计算。它左边的表达式是 x,右边的表达式是 (y && z)(我想我们都在想如果 && 优先,它会像 (a || b) && c 所以它会首先被评估,但这不是它的工作原理)。如果我们想看看实际上是这样的话,我们可以这样修改上面的代码:

 res = expression(a = 8, "One") || expression(a = 16, "Two") && expression(a = 32, "Three");

这等同于 false || (false && false),但编译器不会干扰编译时常量。结果是:

One called with 8
Two called with 16
false 16

首先,计算 ||,然后计算 && 的左侧。这个 returns false,而 false && ? 永远是 false,所以不计算第三个表达式。但没有违反优先规则。我希望这已经消除了任何混乱。如果没有,我很乐意在聊天中继续讨论并更新我的答案。因为我们从原始代码中知道,如果第一个表达式为真,|| returns 为真并且短路,我们可以说 a || b && c 没有组合成 (a || b) && c.