为什么在检查数组引用表达式是否为 null 之前计算 Java 数组索引表达式?

2022-09-01 05:08:11

根据 JLS,数组访问表达式的运行时计算行为如下:

  1. 首先,计算数组引用表达式。如果此计算突然完成,则数组访问由于同样的原因突然完成,并且不计算索引表达式。
  2. 否则,将计算索引表达式。如果此评估突然完成,则数组访问将出于同样的原因突然完成。
  3. 否则,如果数组引用表达式的值为 null,则引发 NullPointerException。

所以这段代码将打印:java.lang.NullPointerException,index=2

class Test3 {
    public static void main(String[] args) {
        int index = 1;
        try {
            nada()[index = 2]++;
        } catch (Exception e) {
            System.out.println(e + ", index=" + index);
        }
    }

    static int[] nada() {
        return null;
    }
}

问题是:出于什么原因,我们需要首先计算表达式,而不是在数组引用计算为 null 后只抛出 NullPointerException?或者换句话说 - 为什么顺序是1,2,3而不是1,3,2?index = 2


答案 1

数组访问表达式有两个子表达式:

数组访问表达式包含两个子表达式:数组引用表达式(左括号前)和索引表达式(括号内)。

这两个子表达式在数组访问表达式本身之前计算,以便计算表达式。

计算两个子表达式后

nada()[index = 2]++;

成为

null[2]++;

只有现在,表达式才会被计算并抛出。NullPointerException

这与Java中大多数表达式的评估是一致的(我能想到的唯一反例是短路运算符,如&&和||)。

例如,如果进行以下方法调用:

firstMethod().secondMethod(i = 2);

首先计算 和 ,然后才将如果计算到 。firstMethod()i = 2NullPointerExceptionfirstMethod()null


答案 2

这是因为在生成的字节码中没有显式空检查。

nada()[index = 2]++;

转换为以下字节代码:

// evaluate the array reference expression
  INVOKESTATIC Test3.nada ()[I
// evaluate the index expression
  ICONST_2
  DUP
  ISTORE 1
// access the array
// if the array reference expression was null, the IALOAD operation will throw a null pointer exception
  DUP2
  IALOAD
  ICONST_1
  IADD
  IASTORE

推荐