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++是先计算的(增加i1),然后是方法调用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