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

时间:2017-03-17 10:01:09

标签: java language-specifications

请考虑以下代码:

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

根据JLS Sec 15.7.1

,我原本希望这会抛出NullPointerException
  

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

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

为什么?

1 个答案:

答案 0 :(得分:4)

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