当前位置: 首页 > 知识库问答 >
问题:

关于‘’的混淆示例

鲜于高明
2023-03-14

我正在测试

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

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

if(expr1 || expr2 && expr3);

应评估为:

if(expr1 || (expr2 && expr3));

所以expr2

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

这证明只有a1


共有3个答案

司马高明
2023-03-14

优先:

boolean result = a || b && c

为了根据优先级规则获得正确的值,编译器必须在逻辑上计算如下:

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

这说明了你的观点

短路:

对于任何布尔表达式:

a || b

如果a的计算结果为true,则结果不取决于b。当atrue时,b的计算结果是true还是false并不重要。因此,编译器执行:

a || b && c

好像它是这样写的:

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

请注意,以这种方式执行,优先规则仍然受到尊重。表达式不计算为(a||b)

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

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

如果不进行短路计算,布尔表达式可能会抛出NullPointerException。但是,由于短路计算的原因,此表达式在编写时永远不会抛出NullPointerException

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

满元凯
2023-03-14

第一行打印true,因为|运算符短路。

a1

expr2应在expr1之前进行评估

不正确,因为运算符优先级会影响表达式的结构,而不是计算顺序(在大多数情况下)。如果您要重新排序表达式,使其为(expr2

濮阳宜
2023-03-14

表达式是短路。从链接:

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

它看到条件的其余部分并不重要,因为|的一个操作数已经为真(10)

您可以使用按位

但是,(expr2

不可以。您必须将优先级和求值顺序的概念分开。

>

  • 优先级:指定表达式的括号,而不是表达式的求值顺序。例如:

      true || false && false
    

    由于

      true || (false && false)
    

    这并不意味着在Java的情况下首先计算括号中的内容。优先级只是澄清了运算符的操作数是什么,在本例中为falsefalse,在本例中:

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

    的操作数

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

    在本例中,您的示例:

    true || false && false
    

    如前所述,由于优先级而变成这样:

    true || (false && false)
    

    但是Java与C、JavaScript或其他许多语言不同,它的计算方式是从左到右的。根据Java语言规范:

    Java编程语言保证运算符的操作数按特定的求值顺序求值,即从左到右。

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

    因此,当你有:

    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。整个表达式从左到右进行计算。优先级仅通过括号指示运算符的操作数,而不是计算顺序。

    进一步阅读Eric Lippert关于优先性、结合性和求值顺序的解释性文章(如Daniel Pryden所述),它澄清了许多困惑。

    主要的结论是,优先级并不决定表达式的求值内容。它只指示表达式应如何插入括号。另一方面,求值顺序准确地告诉我们表达式是如何求值的,在Java中,求值顺序总是从左到右。

  •  类似资料:
    • 我正在阅读关于线程的oracle java教程,我看到了这个示例 当有人可能使用这些方法签入他们的代码时?thread.interrupt(标志)是静态的,所以通过上面的检查,我们实际上检查是否有任何踏面被中断了?即使是这样,根据教程,当线程通过调用静态方法thread.interrupted检查中断时,中断状态将被清除。或任何通过抛出InterruptedException退出的方法在这样做时将

    • 我对两个说明都有点困惑。首先,让我们放弃扫描值为0和未定义的/bsr或bitsize/lzcnt结果的特殊情况--这个区别很明显,不是我的问题的一部分。 让我们取二进制值 计数,返回到位0(即lsb)的索引或距离。 如果CPU上没有可用的BMI,如何模拟为?或者在的情况下位0是MSB?英特尔规范中的两个“代码操作”也不同,一个从左边计数或索引,另一个从右边计数或索引。 也许有人能对此有所了解,我没

    • 关于MVP和MVVM的几点混淆 在问这个问题之前,我读了一篇关于MVP和MVVM的文章。(例如:http://martinfowler.com/eaadev/uiarchs.html) null 例如,富客户端应用程序将同时使用本地数据库和远程服务。 在业务层,有两种对象! 域对象,它是从我的本地数据库中读取的。 在MVP中,只有一个“模型层”,所以我可以在这个模型层中管理和存储domian对象和

    • 在类图中,用户可以参加多个考试。考试由几个类别组成。每个类别都有许多问题。每个问题都有许多选择。 我要问的两个地方是用户与考试交互的地方。 我的一个朋友说我应该在用户和考试之间建立一个叫做考试的关联类。我想,作为属性,它可以有DateTime和Score。为什么考试不能存储分数并有一个完成日期?(这难道不意味着当考试结束时,它只会改变状态吗?)有了这个设计,我想它也必须有一个用户属性。这将结合用户

    • [免责声明:首先,我知道有很多关于drawable和mipmap文件夹的帖子,我读了很多] 在《Android Programming-The Big Nerd Ranch Guide》一书中有一个示例,其中我必须在上的一些文本旁边插入一个图标。对于更高分辨率的屏幕,包含多个大小的图标是有意义的。在他们的解决方案中,他们的所有图像都在许多不同的可绘制文件夹中,具有不同的图像分辨率大小。 然而,我对

    • 但现在它打印出“SAT 5月18日19:00:00 CDT 2013”(因为我的本地机器在CDT上),但我希望结果是“SAT 5月18日24:00:00 CDT 2013”。如何将日期字符串“1368921600000”解释为EST日期?谢谢-戴夫