我从测验中得到了一个操作顺序问题,但解释并不完全有用。这是代码:
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;
[在postfix之后,优先级给予前缀]。
((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 ++是的,这是有道理的。但我认为&&在||之前。
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
来自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,因此不会计算第三个表达式。但是没有违反优先规则。我希望这可以解决任何困惑。如果没有,我很高兴继续在聊天中讨论并更新我的答案。因为我们从原始代码知道如果第一个表达式为真,||
返回true和短路,我们可以说a || b && c
没有组合成(a || b) && c
。
最后当(((a ++)== 2)|| false)[a = 1]然后作为||运算符的优先级低于++,所以这里a将变为3 ..然后它将打印a = 3虽然它是一个短路运算符仍然必须执行++运算符1st。