Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java中的求值顺序规则是什么?_Java_Operator Precedence - Fatal编程技术网

Java中的求值顺序规则是什么?

Java中的求值顺序规则是什么?,java,operator-precedence,Java,Operator Precedence,我正在阅读一些Java文本,得到以下代码: int[] a = {4,4}; int b = 1; a[b] = b = 0; 在文中,作者没有给出明确的解释,最后一行的效果是:a[1]=0 我不太确定我是否理解:评估是如何发生的?您的代码相当于: int[] a = {4,4}; int b = 1; c = b; b = 0; a[c] = b; 这就解释了结果。让我说得非常清楚,因为人们一直误解这一点: 子表达式的求值顺序与关联性和优先级无关。关联性和优先级决定运算符的执行顺序,但不决

我正在阅读一些Java文本,得到以下代码:

int[] a = {4,4};
int b = 1;
a[b] = b = 0;
在文中,作者没有给出明确的解释,最后一行的效果是:
a[1]=0


我不太确定我是否理解:评估是如何发生的?

您的代码相当于:

int[] a = {4,4};
int b = 1;
c = b;
b = 0;
a[c] = b;

这就解释了结果。

让我说得非常清楚,因为人们一直误解这一点:

子表达式的求值顺序与关联性和优先级无关。关联性和优先级决定运算符的执行顺序,但不决定子表达式的求值顺序。您的问题是关于子表达式的求值顺序

考虑
A()+B()+C()*D()
。乘法的优先级高于加法,加法是左联想的,因此这相当于
(A()+B())+(C()*D())
,但知道这只会告诉您第一个加法将在第二个加法之前发生,乘法将在第二个加法之前发生它不会告诉您调用A()、B()、C()和D()的顺序(它也不会告诉您乘法是在第一次加法之前还是之后发生的。)完全可以通过将其编译为:

d = D()          // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b      // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last
所有的优先级和结合性规则都在这里遵循——第一个加法发生在第二个加法之前,乘法发生在第二个加法之前。显然,我们可以按任意顺序调用A()、B()、C()和D(),并且仍然遵守优先级和关联性规则

我们需要一个与优先级和结合性规则无关的规则来解释子表达式的求值顺序Java(和C#)中的相关规则是“从左到右计算子表达式”。由于A()出现在C()的左侧,因此首先计算A(),而不管C()涉及乘法,A()仅涉及加法

所以现在你有足够的信息来回答你的问题。在
a[b]=b=0
中,关联性规则说这是
a[b]=(b=0)
但这并不意味着
b=0
首先运行!优先级规则规定索引的优先级高于赋值,但这并不意味着索引器在最右边的赋值之前运行

(更新:这个答案的早期版本在下面的部分中有一些小的、实际上不重要的遗漏,我已经纠正了这些遗漏。我还写了一篇博客文章,描述了为什么这些规则在Java和C中是合理的:)

优先级和关联性只告诉我们,将零赋值给
b
必须发生在赋值给
a[b]
之前,因为赋值为零会计算在索引操作中赋值的值。优先级和关联性本身并不能说明是在
b=0
之前还是之后计算
a[b]

同样,这与:
A()[B()]=C()
——我们只知道索引必须在赋值之前进行。我们不知道是A()、B()还是C()首先基于优先级和关联性运行。我们需要另一条规则来告诉我们

规则是,“当你可以选择先做什么时,总是从左到右”。然而,在这个特定场景中有一个有趣的折痕由空集合或超出范围索引引起的抛出异常的副作用是否被视为赋值左侧计算的一部分,或赋值本身计算的一部分?Java选择后者。(当然,只有在代码已经错误的情况下,这才是一个重要的区别,因为正确的代码首先不会取消对null的引用或传递错误的索引。)

那么会发生什么呢

  • a[b]
    位于
    b=0
    的左侧,因此
    a[b]
    首先运行,导致
    a[1]
    。但是,检查此索引操作的有效性会延迟
  • 然后发生
    b=0
  • 然后验证
    a
    是否有效,以及
    a[1]
    是否在范围内
  • 最后将值赋值给
    a[1]

这样,尽管在这种特定的情况下,对于那些不应该出现在正确代码中的错误案例,有一些细微之处需要考虑,一般情况下,你可以推理:<强>左到右之前发生的事情发生在“右/强”之前。这就是你要找的规则。关于优先性和关联性的讨论既令人困惑又无关紧要

人们总是把这件事弄错,即使是那些应该更清楚的人。我编辑了太多错误地表述规则的编程书籍,因此许多人对优先级/关联性和求值顺序之间的关系持有完全错误的信念也就不足为奇了——也就是说,实际上没有这种关系;他们是独立的

如果您对这个主题感兴趣,请参阅我关于这个主题的文章,以便进一步阅读:


它们是关于C#,但这些东西中的大多数同样适用于Java。

Eric Lippert的精妙答案并没有很好的帮助,因为它谈论的是另一种语言。这就是Java,Java语言规范是语义的最终描述。特别是,它是相关的,因为它描述了
=
操作符的求值顺序(我们都知道它是右关联的,是吗?)。将其缩减到我们在这个问题中关心的部分:

如果左侧操作数表示
a[b] = b = 0;
(a[b]) = b = 0;
(a[b]) = (b=0);
System.out.println(3+100/10*2-13);
100/10*2
100/10*2
=100/10
=10*2
=20
=3+20-13
=3+20
=23
=23-13
=10