关于'&&'和'||'的令人困惑的例子 优先权

Abd*_*ane 7 java operator-precedence java-7

我测试的优先级&&,并||和我有令人困惑的例子.在Java中,&&运算符优先级高于运算符||.

所以,如果我们有这3个表达式:

//expr1  = true , expr2 = false; expr3 = false;

if(expr1 || expr2 && expr3);
Run Code Online (Sandbox Code Playgroud)

应评估为:

if(expr1 || (expr2 && expr3));
Run Code Online (Sandbox Code Playgroud)

所以expr2 && expr3应该在之前进行评估expr1.但是,这个例子:

int a1 = 10;
int a2 = 20;
System.out.println(a1 < a2 || ++a1 > a2 && ++a2 < a1);
System.out.println(a1);
System.out.println(a2);
Run Code Online (Sandbox Code Playgroud)

输出:

true
10
20
Run Code Online (Sandbox Code Playgroud)

这证明只有 a1 < a2被评估.你能解释为什么会这样吗?

Li3*_*357 13

表达是短路的.从链接:

当AND函数的第一个参数求值为false时,整数值必须为false; 当OR函数的第一个参数的计算结果为true时,整数值必须为true.

它看到其余的条件无关紧要,因为其中一个操作数||已经为真(10 <20).如果其中一个操作数为真,则无论其余条件是什么,都是如此.

您可以按位使用&|防止这种情况发生.


但是,(expr2 && expr3)应该在expr1之前评估,不是吗?

不可以.您必须将优先级评估顺序的概念分开.

  • 优先级:指示表达式的括号,而不是表达式的计算顺序.举个例子:

    true || false && false
    
    Run Code Online (Sandbox Code Playgroud)

    括号括起来因为&&优先级高于||:

    true || (false && false)
    
    Run Code Online (Sandbox Code Playgroud)

    这并不意味着在Java的情况下首先评估括号中的内容.优先级只是阐明什么操作的操作数是,在这种情况下falsefalse,其中在这种情况下:

    (true || false) && (false || false)
    
    Run Code Online (Sandbox Code Playgroud)

    &&true和的操作数,而false不是falsefalse.

  • 评估顺序:描述每个操作数的评估顺序和运算符的应用顺序,有时是语言特定的.这决定了如何评估表达式,与优先级不同.

在这种情况下,您的示例:

true || false && false
Run Code Online (Sandbox Code Playgroud)

如前所述,由于优先权而成为此:

true || (false && false)
Run Code Online (Sandbox Code Playgroud)

但是,与C++,JavaScript或许多其他语言不同,Java具有严格的从左到右的评估.根据Java语言规范:

15.7.评估订单

Java编程语言保证运算符的操作数似乎以特定的评估顺序进行评估,即从左到右.

15.7.1.首先评估左手操作数

在评估右侧操作数的任何部分之前,似乎完全评估了二元运算符的左侧操作数.

所以,当你有:

true || (false && false)
Run Code Online (Sandbox Code Playgroud)

Java 首先评估左操作数,结果证明是true.然后整个条件短路.||括号中的右操作数永远不会被评估.你的另一个例子也是如此:

a1 < a2 || (++a1 > a2 && ++a2 < a1)
           ^^^^^^^^^^^^^^^^^^^^^^^^
           Step 0, precedence and parenthesization
Run Code Online (Sandbox Code Playgroud)
a1 < a2 || (++a1 > a2 && ++a2 < a1)
^^^^^^^
Step 1, left operand evaluated, variables resolved to values 10 and 20, condition is true
Run Code Online (Sandbox Code Playgroud)
true || (++a1 > a2 && ++a2 < a1)
^^^^
Step 2, short circuits, left operand is not evaluated
Run Code Online (Sandbox Code Playgroud)

再举一个更复杂的例子:

false || false || true && (false || true) || false
Run Code Online (Sandbox Code Playgroud)

由于优先权,它变为:

false || false || (true && (false || true)) || false
Run Code Online (Sandbox Code Playgroud)

然后,评估从左到右开始:

false || false || (true && (false || true)) || false
^^^^^^^^^^^^^^
Step 1, false || false, does not short circuit, right operand is evaluated, is false
Run Code Online (Sandbox Code Playgroud)
false || (true && (false || true)) || false
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Step 2, false || (true && (false || true)), does not short circuit, right operand is evaluated
Step 2A, (true && (false || true)), does not short circuit, right operand is evaluated
Step 2B, (false || true), does not short circuit, right operand is evaluated, is true
Step 2C, (true && true), does not short circuit, right operand is evaluated, is true
Step 2D, false || true, does not short circuit, right operand is evaluated, is true
Run Code Online (Sandbox Code Playgroud)
true || false
^^^^
Step 3, true || false short circuits, right operand is not evaluated, is true
Run Code Online (Sandbox Code Playgroud)

因此整个表达式评估为true.整个表达从整个过程中从左到右进行评估.优先级仅通过括号而不是评估顺序来指定运算符的操作数.

进一步阅读Eric Lippert关于优先级与关联性与评估顺序的解释性文章,如Daniel Pryden所述,它清除了很多混乱.

主要的一点是,优先级并不决定表达式的评估.它只规定表达式应如何括起来.另一方面,评估顺序告诉我们如何评估表达式,并且在Java的情况下总是从左到右.


Sam*_*s33 6

第一行打印true是因为||操作员短路.

a1 < a2是的,因此不需要计算布尔表达式的其余部分并true返回.

应该在expr1之前评估expr2

是不正确的,因为运算符优先级会影响表达式的结构,而不会影响评估顺序(在大多数情况下).如果你要对表达式进行重新排序(expr2 && expr3) || expr1,那么是的,expr2之前会进行评估expr1


sco*_*ttb 5

优先级:

boolean result = a || b && c
Run Code Online (Sandbox Code Playgroud)

为了根据优先级规则获得正确的值,编译器必须在逻辑上将其评估为:

boolean x = b && c
boolean result = a || x
Run Code Online (Sandbox Code Playgroud)

这说明了b && c在计算之前必须进行评估的观点result.但是,在布尔代数中,可以编写其结果不依赖于所有操作数的表达式.利用这一事实来实现称为短路的编译器优化.

短路:

对于任何布尔表达式:

a || b
Run Code Online (Sandbox Code Playgroud)

结果不依赖于b是否a评估true.如果atrue,如果没有关系b计算结果为真或假.因此,编译器执行:

a || b && c
Run Code Online (Sandbox Code Playgroud)

好像它已被写入:

boolean result = a;
if (!a) result = b && c;
Run Code Online (Sandbox Code Playgroud)

请注意,以这种方式执行,仍然遵守优先规则.表达式未评估为(a || b) && c.由于表达的整体结果并不取决于(b && c)在该情况下a是真实的,b && c简直是永远不会计算.

这是一件好事.当一个操作数的正确性取决于另一个操作数的正确性时,短路允许您编写正确的程序.例如:

if (myString == null || !myString.isEmpty() && myString != "break") return;
Run Code Online (Sandbox Code Playgroud)

没有短路评估,布尔表达式可以抛出一个NullPointerException.但是,由于短路评估,这个表达式,如所写,永远不会抛出NullPointerException.

短路也可以用作性能优化.如果评估一个操作数非常昂贵,那么首先评估另一个操作数可以节省评估操作数所需的执行时间,该操作数的值不会影响整个表达式的最终结果.