Java中是否保证从左到右的操作顺序?

12

考虑以下函数:

public static final int F(int a, int b) {
    a = a - 1 + b;
    // and some stuff
    return a;
}

实现JVM的过程中是否必须在执行+ b之前执行- 1

如果我们连接了系统分析器到JVM,我们会在+ 1操作执行之前看到+ b操作被执行吗?


不是很相关,但是 input 是什么? - Radu Murzea
1
@Pacerier - 这不是一个简短,自包含,正确(可编译),示例。它不是自包含的(没有类声明,也没有主函数)。它不能被编译(它包含了一个拼写错误--你是复制粘贴的吗?)。 - Robᵩ
@Robᵩ,不,如果是复制粘贴的话,你很容易就能在Google上找到它。我不同意"正确的例子" = "可编译的例子"。实际上,我的第二个"C"与"可编译的"毫不相关。所有函数的正确例子在Java中都是不能编译的,即使它们很短且自包含。 - Pacerier
我相信,这个示例更好: int a = (buffer.get() & 255) | (buffer.get() & 255) << 8; - Chameleon
10个回答

8

其实,我不同意其他回答的观点。人们提到的JLS §15.7是讨论操作数的评估。也就是说,在表达式中

x = foo() - 1 + bar()

方法的调用顺序取决于其在代码中的位置。

相关部分为§15.7.3,其中规定:

除非可以证明替换后的表达式在值和可观察副作用方面等效,否则实现不得利用诸如结合律之类的代数恒等式将表达式重写为更方便的计算顺序。

由于表达式x = x - 1 + q在所有方面等效于x = x + q - 1,因此符合规范的实现允许重写该表达式(如果出于某种原因它认为这样更有效)。


@Pacerier,请注意以下内容。一般来说,foo() 可以影响 bar(),因此 JVM 应该尊重顺序。对于 i > 0 && x/i > 3,同样适用。代数上讲,a && b 等价于 b && a,但是将 i > 0 放在前面可以避免第二个表达式中出现 NPE 错误。 - rdllopes

6

是的,它使用Java语言规范第15.7节。

Java编程语言保证运算符的操作数按照特定的顺序从左到右进行评估。

建议代码不要过分依赖于此规范。当每个表达式仅包含一个副作用时,其最外层操作更清晰,并且代码不依赖于由于表达式从左到右的评估而产生的异常。


“Appear to be evaluated” - Java是否有类似于C和C++标准的“as-if”规则?也就是说,只要JVM的行为与标准的行为不可区分,它就可以随心所欲地执行任何操作? - Robᵩ
2
@Rob 是的,它可以。它可以按照任何顺序执行操作,只要对于执行线程来说,它们就像是按照指定的顺序执行的一样。然而,从其他线程的角度来看,它们可能会出现乱序的情况。这就是为什么多个线程必须使用明确定义的内存屏障进行协调的原因。 - erickson

2
根据这里的内容:
相同行上的运算符具有相等的优先级。当相等优先级的运算符出现在同一表达式中时,必须有规则来控制哪个先被计算。除赋值运算符外,所有二元运算符都从左到右计算;赋值运算符从右到左计算。
所以是的。

2

是的,尽管更重要的问题是这真的重要吗?在任何科学数学运算中,减法和加法具有相同的优先级并且可交换。也就是说:

x = input - 1 + q;

x = input + q - 1;

是一样的。


我的意思是我不喜欢它溢出,但是想一想,即使它溢出了,结果也是一样的。 - Pacerier
无论如何,您可能会遇到溢出 - 即使输入是MIN_INTEGER或q + input = MAX_INTEGER。在您的示例中,没有真正的方法来防止这种情况发生。 - Eric B.

2

2
实际上,重要的不是JVM的实现,因为它们只是执行来自类文件的指令列表。

2
您可以查看Java运算符优先级:http://bmanolov.free.fr/javaoperators.php。因为+-具有相同的优先级,它们将按照它们出现的顺序执行。
如果您想更清楚地了解哪个操作先发生(或者如果您想打破内置优先级),则可以(并且应该)使用括号(()),如下所示: x = (a - b) + (c * (d - e))

1

是的,即使它不会影响加/减的结果,它也总是会这样。


1

在执行X + q - 1时,我觉得总会有副作用, 而当你应该执行X - 1 + q时。

当x + q的和超过Bignum 1时,从左到右的执行将成功...错序执行将产生溢出。

因此,无序执行具有副作用。

除非无序执行本身捕获了溢出,然后在需要时重新按正确顺序计算以避免副作用。


0

这里需要强调的是,Java确实具有基于基本算术运算顺序的操作符优先级。要查看完整优先级列表,请参阅source


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接