5

考虑以下代码:

int[] r = null;
r[0] = 1 % 0;

NullPointerException根据JLS Sec 15.7.1,我原以为这会引发:

在评估右侧操作数的任何部分之前,二元运算符的左侧操作数似乎已被完全评估。

=是一个二元运算符(在JLS Sec 15.2中显示- JLS Sec 15.26 描述了赋值运算符),并且完全评估左侧操作数将导致NullPointerException. 但是,ArithmeticException抛出 an ,表示在完全评估左侧操作数之前评估右侧操作数。

为什么?

4

1 回答 1

5

简单赋值运算符的规范描述了这种行为:

...

如果左侧操作数是数组访问表达式(第 15.10.3 节),可能包含在一对或多对括号中,则:

  • 首先,评估左侧操作数数组访问表达式的数组引用子表达式。如果这个求值突然完成,那么赋值表达式也会因为同样的原因而突然完成;(左侧操作数数组访问表达式的)索引子表达式和右侧操作数不被计算并且不发生赋值。

这正常完成。

  • 否则,计算左侧操作数数组访问表达式的索引子表达式。如果此评估突然完成,则赋值表达式出于同样的原因突然完成,并且不会评估右侧操作数并且不会发生赋值。

这正常完成。

  • 否则,评估右侧操作数。如果这个求值突然完成,那么赋值表达式也会因为同样的原因而突然完成并且没有赋值发生。

这突然完成,与ArithmeticException

  • 否则,如果数组引用子表达式的值为 null,则不会发生赋值并抛出 NullPointerException。

这永远不会执行。

因此,第 15.7.1 节的引用似乎存在不一致或过度简化。


有趣的是,复合赋值运算符没有观察到相同的行为,例如

int[] arr = null;
arr[0] += 1 % 0;

确实产生一个NullPointerException.

JLS Sec 15.26.2 对此进行了描述。不过,这也许不那么令人惊讶,因为:

形式的复合赋值表达式E1 op= E2等价于E1 = (T) ((E1) op (E2)),其中T是 的类型E1,除了E1只计算一次。

换句话说,这段代码(大致)相当于:

arr[0] = arr[0] + 1 % 0;

所以NullPointerException发生在评估简单赋值的右手操作数时。

于 2017-03-17T10:01:08.840 回答