Java中的逻辑运算顺序令人困惑
Posted
技术标签:
【中文标题】Java中的逻辑运算顺序令人困惑【英文标题】:Order of Operations with Logic in Java Confused 【发布时间】:2019-08-03 15:21:36 【问题描述】:我有一个测验中的操作顺序问题,但解释并不完全有帮助。代码如下:
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;
[后缀的优先级高于其他运算符]。
(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=false]。
(2 == 2) || (((--a) == 2) && ((--a) == 2));
[a=3, res=false]。 true || (((--a) == 2) && ((--a) == 2));
[a=3, res=false]。
||是一个短路运算符,因此不需要计算右边的表达式。
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++ 是的,这是有道理的。但我认为 && 在 || 之前。
【问题讨论】:
【参考方案1】:来自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
?后增量意味着 a 被读取为 2。然后评估该表达式。 2 == 2
,这是真的。短路意味着表达式的其余部分从不计算。
所以,上面的代码基本上都是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
首先,评估||
,然后评估&&
的左侧。这将返回 false,而false && ?
将始终为 false,因此不计算第三个表达式。但是没有违反优先规则。我希望这已经消除了任何困惑。如果没有,我很乐意在聊天中继续讨论并更新我的答案。因为我们从原始代码中知道,如果第一个表达式为真,||
返回真和短路,我们可以说a || b && c
没有组合成(a || b) && c
。
【讨论】:
但是如果 || 会短路有更高的优先级...不是 && 有更高的优先级吗?所以在它之前||短路,需要处理&&? 没有。它首先评估||
的左侧。我将在我的答案中添加一些测试代码
据此我知道它首先评估左侧,但根据我所看到的一切,&& 具有更高的优先级,这意味着它需要先评估 &&...
所以,我认为编译器可能正在优化代码,因为它可以看到 a
在编译时为 2。我尝试使用 Math.random() 将 a
设置为编译器在编译时看不到的东西,但如果该随机数为 2,则答案是相同的。我唯一的结论是它首先读取||
,尽管它们的优先级明显更高。
我试过 boolean bar = foo(true, 1) ||富(假,2)&&富(真,3);其中 foo 是打印第二个参数并返回第一个参数的静态布尔方法。它所做的只是打印 1。这意味着它在做真实的 || foo(false, 2) && foo(true, 3) 和短路而无需打扰 && 好像 ||和 && 只是从左到右。富(真,3)&&富(真,1)|| foo(false, 2) -> 3 1. 所以无论是哪种顺序,它似乎都是从左到右的。【参考方案2】:
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
【讨论】:
但是括号会导致我这样做......并得到0? 你是怎么得到 0 的?这是不可能的 第 3 步结果 a = 0。a 在 4 5 和 6 中不再更改 好的,我想我知道你为什么仍然困惑。优先级有点像分组。因此,它将逐步执行。你为什么不试着颠倒等式并像我一样按照步骤告诉我为什么它得到 2. res = --a == 2 && --a == 2 ||一个++ == 2; (记住要遵循算子级别和关联性,然后你就会明白短路是什么) 1. a++ -> --a == 2 && --a == 2 || 2 == 2 其中 a = 3 //换行符// 2. --a -> 1 == 2 && --a == 2 || 2 == 2 其中 a = 1 //换行符// 3. 1 == 2 && 0 == 2 || 2 == 2 where a = 0 //linebreak false && false ||真 -> 假【参考方案3】:最后当(((a++) == 2) || false) [a = 1] 然后作为 || 完成运算符的优先级低于 ++ 所以这里 a 将变为 3 .. 然后它将打印 a=3 虽然它是一个短路运算符,但它仍然必须执行 ++ 运算符 1st。
【讨论】:
以上是关于Java中的逻辑运算顺序令人困惑的主要内容,如果未能解决你的问题,请参考以下文章