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
.
我有一个来自测验的操作顺序问题,解释并不是很有帮助。这是代码:
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
.