【问题标题】:Array reference expression not completely evaluated数组引用表达式未完全计算
【发布时间】:2026-01-04 03:10:02
【问题描述】:

请各位大神指教一下:

片段 1:

public class ArrayKoPo {

    public static int[] getArray() {
        return null;
    }

    public static void main(String args[]) {
        int i = 0;
        try {
            int j = getArray()[i++];
        } catch (Exception e) {
            System.out.println(i); //prints 1 <---- This one I expected.
        }
    }
}

片段 2:

public class ArrayKoPo {

    public static int[][] getArray() {
        return null;
    }

    public static void main(String args[]) {
        int i = 0;
        try {
            int j = getArray()[i++][i++];
        } catch (Exception e) {
            System.out.println(i); //still prints 1 <---- This one I don't understand. I thought 2 will be printed.
        }
    }
}

为什么变量 i 在第二个代码块中没有增加两次?

我错过了什么?

谢谢。

【问题讨论】:

  • getArray()[i++] 触发了NullPointerException,因此它无法在getArray()[i++] 上索引[i++]。 Java 从左到右计算 :-)
  • 我明白了。这就是我在那里错过的。我认为括号内的所有表达式将在整个索引操作发生之前首先(从左到右)进行评估,但正如您所指出的,索引操作从左到右“按索引”发生。谢谢你解释! :)
  • 我已经用充足的证据修改了我的答案!

标签: java arrays post-increment


【解决方案1】:

my comment中所述...

首先,重要的是要注意 Java 从左到右进行计算。我们发现getArray()[i++] 尝试访问被视为数组的null 的元素,从而产生NullPointerException。在评估外部数组访问表达式(其索引计算为 i++)之前,此异常会中断表达式 getArray()[i++][i++] 的评估,因此第二个增量永远不会发生 :-)


这与描述数组访问表达式的§15.13 of the Java Language Specification 一致。

ArrayAccess:
    ExpressionName [ Expression ]
    PrimaryNoNewArray [ Expression ]

表达式求值的分步过程在§15.13.1 Run-time Evaluation of Array Access中明确说明:

使用以下过程计算数组访问表达式:

  • 首先,计算数组引用表达式。如果这个求值突然完成,那么数组访问也会因为同样的原因而突然完成并且索引表达式不会被求值。

  • 否则,将评估索引表达式。如果这个求值突然完成,那么数组访问也会因为同样的原因而突然完成。

  • 否则,如果数组引用表达式的值为null,则抛出NullPointerException

  • 否则,数组引用表达式的值确实引用了一个数组。如果索引表达式的值小于零,或者大于等于数组的length,则抛出ArrayIndexOutOfBoundsException

  • 否则,数组访问的结果是T类型的变量,在数组中,由索引表达式的值选择。


现在,要理解我们的结果,您必须意识到 Java 多维数组本质上是锯齿状,并且实现为数组的数组int[][] 只是 int[] 的数组。

手头真正的表达式涉及两个数组访问表达式,即一个外部数组访问表达式,其索引表达式为i++,而数组引用表达式本身就是一个数组访问表达式,即其引用表达式为getArray(),其索引表达式为i++

按照计算规则,要计算表达式getArray()[i++][i++],我们首先必须计算数组引用表达式,即getArray()[i++]。事实证明,这本身就是一个数组访问和表达式,我们必须应用相同的规则。评估getArray() 会得到null。索引表达式i++ 也完全完成(递增i),然后到达此时抛出NullPointerException 的步骤。由于外部数组访问表达式的数组引用表达式突然结束,所以它也突然结束,因此外部访问表达式的索引表达式 (i++) 不会被计算,这意味着 i 只会增加一次。

...现在你知道了;-)

【讨论】:

  • 哇!很好的解释!谢谢!
【解决方案2】:

我相信它是这样发生的:

  1. 程序运行到 getArray():返回 null。
  2. 然后程序转到方括号,计算参数,并尝试进入数组。 (我不熟悉的Java魔法就发生在这里)
  3. 尝试失败并在查看第二个索引运算符之前生成异常。

如果我们要以另一种方式放置您的第二个 sn-p,它应该等效于以下内容(从而生成与第一个 sn-p 相同的结果):

public class ArrayKoPo {

    public static int[][] getArray() {
        return null;
    }

    public static void main(String args[]) {
        int i = 0;
        try {
            int[] j = getArray()[i++];
            int k = j[i++];
        } catch (Exception e) {
            System.out.println(i);
        }
    }
}

【讨论】: