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

时间:2016-10-16 15:09:07

标签: java java-7 operator-precedence

我正在测试 throw er; // Unhandled stream error in pipe. ^ Error: EMFILE: too many open files, open 'C:\Users\Administrator\Desktop\graby\image.jpg' at Error (native) [nodemon] app crashed - waiting for file changes before starting... &&之间的优先级,我有一个令人困惑的例子。在Java中,||的运算符优先级高于运算符&&

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

||

应评估为:

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

if(expr1 || expr2 && expr3);

因此if(expr1 || (expr2 && expr3)); 应在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);

证明只评估true 10 20 。 你能解释为什么会这样吗?

4 个答案:

答案 0 :(得分:13)

表达式为short-circuiting。从链接:

  

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

它发现其余条件并不重要,因为||的一个操作数已经为真(10 <20)。如果其中一个操作数是真的,那么无论其他条件是什么,它都是真的。

您可以使用按位&|来阻止此操作。

  

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

没有。您必须将优先级评估顺序的概念分开。

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

    true || false && false
    

    为括号加以括号,因为&&的优先级高于||

    true || (false && false)
    

    这并不意味着在Java的案例中首先评估括号中的内容。优先级只是澄清运算符的操作数,在本例中为falsefalse,其中 this 情况:

    (true || false) && (false || false)
    

    &&的操作数为truefalse,而不是falsefalse

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

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

true || false && false

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

true || (false && false)

但是,与C ++,JavaScript或许多其他语言不同,Java具有严格的从左到右的评估。根据{{​​3}}:

  

15.7。评估订单

     

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

     

15.7.1。首先评估左手操作数

     

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

所以,当你有:

true || (false && false)

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

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

再举一个更复杂的例子:

false || false || true && (false || true) || false

由于优先权,它变为:

false || false || (true && (false || true)) || false

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

false || false || (true && (false || true)) || false
^^^^^^^^^^^^^^
Step 1, false || false, does not short circuit, right operand is evaluated, is false
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
true || false
^^^^
Step 3, true || false short circuits, right operand is not evaluated, is true

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

Java Language Specification进一步阅读Eric Lippert's explanatory article关于优先级与关联性与评估顺序之间的关联Daniel Pryden,它清除了很多混乱。

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

答案 1 :(得分:6)

由于||运算符短路,第一行打印a1 < a2

true为true,因此不需要评估布尔表达式的其余部分并返回(expr2 && expr3) || expr1

  应在expr1

之前评估

expr2

不正确,因为运算符优先级会影响表达式的结构,而不会影响评估顺序(在大多数情况下)。如果您要重新排序表达式以使其为expr2,那么是,expr1将在class Obj { private: int num; public: Obj(); void setNum(int nuovo_num); int getNum(); };

之前进行评估

答案 2 :(得分:5)

<强>优先级:

boolean result = a || b && c

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

boolean x = b && c
boolean result = a || x

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

短路:

对于任何布尔表达式:

a || b

如果b评估为a,则结果不依赖于true。当atrue时,b评估为true或false无关紧要。因此,编译器执行:

a || b && c
好像已经写好了:

boolean result = a;
if (!a) result = b && c;

请注意,以这种方式执行,仍然遵守优先规则。表达式未计算为(a || b) && c。由于在(b && c)为真的情况下,表达式的整体结果不依赖a,因此b && c永远不会被评估。

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

if (myString == null || !myString.isEmpty() && myString != "break") return;

如果没有短路评估,布尔表达式可能会抛出NullPointerException。但是,由于短路评估,所写的表达式永远不会抛出NullPointerException

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

答案 3 :(得分:3)

即使您在

中使用明确的括号
if (expr1 || (expr2 && expr3))
首先评估

expr1,因为运算符的操作数是从左到右计算的。由于||是一个短路运算符,因此只有当(expr2 && expr3)expr1时,才会评估第二个操作数(在您的情况下为false)。

删除括号后,如果expr1为false,则运算符优先级才会起作用。在这种情况下,&&操作数将在||运算符之前进行求值,其值将是||运算符的第二个操作数。