Java 表达式计算优先级:方法调用 & 数组索引
Java expressions calculation precedence: method invocation & array indexing
在学习java表达式计算顺序的过程中,我遇到了一个现象,我自己也解释不清楚。有两个测验问题。要求定义控制台输出。
示例 1
int[] a = {5, 5};
int b = 1;
a[b] = b = 0;
System.out.println(Arrays.toString(a));
正确的控制台输出是:[5,0]
示例 2
public class MainClass {
static int f1(int i) {
System.out.print(i + ",");
return 0;
}
public static void main(String[] args) {
int i = 0;
i = i++ + f1(i);
System.out.print(i);
}
}
正确的控制台输出是:1,0
据我了解,java 中存在具有有序优先级的运算符组(级别),并且表达式根据运算符优先级进行评估。每个组也有关联性,如果运算符具有相同的优先级,那么它们将按照组关联性指定的顺序进行评估。运算符优先级 table(来自 Cay S. Horstmann - Core Java V.1):
# 运算符 结合性
1 [] 。 ()方法调用从左到右
2! ~ ++ -- + - (type) cast new 从右到左
3 * / % 从左到右
4 + - 从左到右
...
14 = += -= 其余省略 从右到左
通过上面的 table 可以清楚地看出,在示例 1 中,具有最高优先级的运算符是数组索引 a[b]
,然后从右到左计算赋值运算符:b=0
,然后 a[1]=0
。这就是为什么a=[5,0]
。
但是示例 2 让我感到困惑。根据优先级table,具有最高优先级的运算符是f1(i)
方法调用(应该打印0
),然后是一元post-increment i++
(使用当前 i=0
并在其后递增),然后是加法运算符 0+0
,最后是赋值运算符 i=0
。所以,我认为正确的输出是 0,0
.
但实际上并非如此。实际上一元post-增量i++
是先计算的(增加i
到1
),然后是方法调用f1(i)
打印 1
和 returns 0
最后赋值运算符赋值 i=0+0
,所以最终的 i
值是 0
正确答案是 1,0
。
我想这是由于二进制加法运算符的关联性 "from left to right",但在这种情况下,为什么在示例 2 中首先计算加法,但在示例 1 中,最高优先级运算符 a[b]
是先计算?我注意到示例 2 中的所有运算符都在不同的组中,所以我们根本不应该考虑运算符关联性,不是吗?我们不应该只按优先顺序对示例 2 中的所有运算符进行排序,然后按结果顺序对其进行评估吗?
运算符的存在和结合性影响源代码如何被解析为表达式树。但是:任何表达式中的求值顺序仍然是从左到右。
这就是为什么在 i++ + f1(i)
中,我们首先评估 i++
,然后 f1(i)
,然后计算它们的总和。
具有最高优先级的方法调用意味着 i++ + f1(i)
永远不会被解析为 (i++ + f1)(i)
(如果这有意义的话),但总是 i++ + (f1(i))
。优先级不等于"is evaluated before anything else."
你在混淆 evaluation order with precedence。
=
的从右到左结合律意味着
a[b] = b = 0;
被评估为
a[b] = (b = 0);
但计算仍然是从左到右,所以第一个 b
的值在 b
的值更新之前被 计算 .
a[b] = (b = 0) a = { 5, 5 }, b = 1
// evaluate 'b'
a[1] = (b = 0) a = { 5, 5 }, b = 1
// evaluate 'b = 0'
a[1] = 0 a = { 5, 5 }, b = 0
// evaluate 'a[1] = 0'
0 a = { 5, 0 }, b = 0
在学习java表达式计算顺序的过程中,我遇到了一个现象,我自己也解释不清楚。有两个测验问题。要求定义控制台输出。
示例 1
int[] a = {5, 5};
int b = 1;
a[b] = b = 0;
System.out.println(Arrays.toString(a));
正确的控制台输出是:[5,0]
示例 2
public class MainClass {
static int f1(int i) {
System.out.print(i + ",");
return 0;
}
public static void main(String[] args) {
int i = 0;
i = i++ + f1(i);
System.out.print(i);
}
}
正确的控制台输出是:1,0
据我了解,java 中存在具有有序优先级的运算符组(级别),并且表达式根据运算符优先级进行评估。每个组也有关联性,如果运算符具有相同的优先级,那么它们将按照组关联性指定的顺序进行评估。运算符优先级 table(来自 Cay S. Horstmann - Core Java V.1):
# 运算符 结合性 1 [] 。 ()方法调用从左到右 2! ~ ++ -- + - (type) cast new 从右到左 3 * / % 从左到右 4 + - 从左到右 ... 14 = += -= 其余省略 从右到左
通过上面的 table 可以清楚地看出,在示例 1 中,具有最高优先级的运算符是数组索引 a[b]
,然后从右到左计算赋值运算符:b=0
,然后 a[1]=0
。这就是为什么a=[5,0]
。
但是示例 2 让我感到困惑。根据优先级table,具有最高优先级的运算符是f1(i)
方法调用(应该打印0
),然后是一元post-increment i++
(使用当前 i=0
并在其后递增),然后是加法运算符 0+0
,最后是赋值运算符 i=0
。所以,我认为正确的输出是 0,0
.
但实际上并非如此。实际上一元post-增量i++
是先计算的(增加i
到1
),然后是方法调用f1(i)
打印 1
和 returns 0
最后赋值运算符赋值 i=0+0
,所以最终的 i
值是 0
正确答案是 1,0
。
我想这是由于二进制加法运算符的关联性 "from left to right",但在这种情况下,为什么在示例 2 中首先计算加法,但在示例 1 中,最高优先级运算符 a[b]
是先计算?我注意到示例 2 中的所有运算符都在不同的组中,所以我们根本不应该考虑运算符关联性,不是吗?我们不应该只按优先顺序对示例 2 中的所有运算符进行排序,然后按结果顺序对其进行评估吗?
运算符的存在和结合性影响源代码如何被解析为表达式树。但是:任何表达式中的求值顺序仍然是从左到右。
这就是为什么在 i++ + f1(i)
中,我们首先评估 i++
,然后 f1(i)
,然后计算它们的总和。
具有最高优先级的方法调用意味着 i++ + f1(i)
永远不会被解析为 (i++ + f1)(i)
(如果这有意义的话),但总是 i++ + (f1(i))
。优先级不等于"is evaluated before anything else."
你在混淆 evaluation order with precedence。
=
的从右到左结合律意味着
a[b] = b = 0;
被评估为
a[b] = (b = 0);
但计算仍然是从左到右,所以第一个 b
的值在 b
的值更新之前被 计算 .
a[b] = (b = 0) a = { 5, 5 }, b = 1
// evaluate 'b'
a[1] = (b = 0) a = { 5, 5 }, b = 1
// evaluate 'b = 0'
a[1] = 0 a = { 5, 5 }, b = 0
// evaluate 'a[1] = 0'
0 a = { 5, 0 }, b = 0