为啥空数组引用的数组访问表达式不抛出 NullPointerException?

Posted

技术标签:

【中文标题】为啥空数组引用的数组访问表达式不抛出 NullPointerException?【英文标题】:Why doesn't an array access expression of a null array reference throw a NullPointerException?为什么空数组引用的数组访问表达式不抛出 NullPointerException? 【发布时间】:2017-08-08 19:55:28 【问题描述】:

考虑以下代码:

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

根据JLS Sec 15.7.1,我预计这会引发NullPointerException

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

= 是一个二元运算符(如JLS Sec 15.2 所示 - JLS Sec 15.26 描述了赋值运算符),完全评估左侧操作数将产生 NullPointerException。但是,会抛出 ArithmeticException,表示在完全计算左侧操作数之前先计算右侧操作数。

为什么?

【问题讨论】:

【参考方案1】:

the simple assignment operator 的规范描述了这种行为:

...

如果左侧操作数是数组访问表达式(第 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)),其中TE1 的类型,但E1 只计算一次。

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

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

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

【讨论】:

以上是关于为啥空数组引用的数组访问表达式不抛出 NullPointerException?的主要内容,如果未能解决你的问题,请参考以下文章

为啥非空列表会抛出空指针异常?

为啥这段代码不抛出 NullPointerException?

为啥设置 DataSource 时 ComboBox 不抛出异常?

Hystrix 不抛出 HystrixRuntimeException,而是空消息错误

为啥 JPA 重复持久方法不抛出异常?

为啥不抛出异常的代码允许捕获已检查的异常?