为什么比较整数与int可以抛出Java中的NullPointerException?

观察这种情况令我非常困惑:

Integer i = null; String str = null; if (i == null) { //Nothing happens ... } if (str == null) { //Nothing happens } if (i == 0) { //NullPointerException ... } if (str == "0") { //Nothing happens ... } 

所以,因为我认为拳击操作首先执行(即Java尝试从null值提取int值)和比较操作具有较低的优先级,这就是为什么引发exception。

问题是:为什么在Java中以这种方式实现? 为什么拳击具有更高的优先权,然后比较参考? 或者为什么他们没有在拳击之前对null实施validation?

目前,当NullPointerExceptionexception抛出时,它看起来不一致,并且不会抛出真正的对象types。

简答题

关键是这样的:

  • ==两个参考types之间总是参考比较
    • 多数情况下,例如使用IntegerString ,你会想要使用equals
  • ==引用types和数字基本types之间始终是数字比较
    • 引用types将进行拆箱转换
    • 取消装箱null总是抛出NullPointerException
  • 虽然Java对String有许多特殊的处理,但实际上它不是一个原始types

上述语句适用于任何给定的有效 Java代码。 有了这个理解,在你提交的代码片段中没有任何不一致的地方。


长的答案

以下是相关的JLS部分:

JLS 15.21.3参考平等操作符==!=

如果等于运算符的操作数既是引用types又是types,则操作是对象相等的。

这解释了以下内容:

 Integer i = null; String str = null; if (i == null) { // Nothing happens } if (str == null) { // Nothing happens } if (str == "0") { // Nothing happens } 

两个操作数都是引用types,这就是为什么==是引用相等比较。

这也解释了以下几点:

 System.out.println(new Integer(0) == new Integer(0)); // "false" System.out.println("X" == "x".toUpperCase()); // "false" 

==为数字相等, 至less有一个操作数必须是数字types

JLS 15.21.1数值相等运算符==!=

如果等于运算符的操作数都是数字types,或者一个是数字types, 另一个是可以转换为数字types,则对操作数执行二进制数字提升。 如果提升types的操作数是intlong ,则执行整数相等性testing; 如果升级types是float or double,则执行浮点相等性testing。

请注意,二进制数字提升执行值集转换和拆箱转换。

这解释了:

 Integer i = null; if (i == 0) { //NullPointerException } 

下面是有效的Java第2版,第49项:将原语应用于装箱原语的摘录:

总而言之,只要有select,就优先使用原始图元(boxed primitive)。 原始types更简单,更快。 如果你必须使用盒装原语,小心! 自动装箱减less了使用装箱原语的冗长度,但不是危险。 当你的程序比较两个盒装原语与==运算符,它进行身份比较,这几乎肯定不是你想要的。 当你的程序进行混合types的计算时,涉及盒装和非盒装原语,它会取消装箱,当你的程序拆箱时,它会抛出NullPointerException 。 最后,当你的程序设置原始值时,可能会导致代价高昂的不必要的对象创build。

有些地方你别无select,只能使用盒装原语,例如generics,否则你应该认真考虑是否使用盒装原语的决定是合理的。

参考

  • JLS 4.2。 原始types和值
    • 数字types是整型和浮点型。”
  • JLS 5.1.8取消装箱转换
    • “如果一个types是一个数字types,那么这个types就可以被转换成数字types,或者它是一个可以通过拆箱转换转换成数字types的引用types。
    • “取消装箱转换将Integertypes转换为inttypes”
    • “如果rnull ,拆箱转换会抛出一个NullPointerException
  • Java语言指南/自动装箱
  • JLS 15.21.1数值相等运算符==!=
  • JLS 15.21.3参考平等操作符==!=
  • JLS 5.6.2二进制数字促销

相关问题

  • 在Java中比较两个Integers是否会发生自动拆箱?
  • 为什么这些==而不是equals()
  • Java:自动装箱和铸造有什么区别?

相关问题

  • Java和C#中的int和Integer有什么区别?
  • 是否保证新的Integer(i)==我在Java? (是的!盒子是拆箱,不是其他方式!)
  • 为什么int num = Integer.getInteger("123")抛出NullPointerException ? (!!!)
  • Java的noob:generics只有对象? (是的,很不幸的)
  • Java String.equals==

由于自动装箱 ,您的NPE示例与此代码等效:

if ( i.intValue( ) == 0 )

因此,如果inull NPE。

 if (i == 0) { //NullPointerException ... } 

我是一个整数,0是一个整数,所以在做什么是这样的

 i.intValue() == 0 

这导致nullPointer,因为我是空的。 对于string我们没有这个操作,那就是为什么没有例外。

Java的制造者可能已经定义了==运算符来直接作用于不同types的操作数,在这种情况下给定Integer I; int i; Integer I; int i; 比较I==i; 可以问一个问题:“ I是否参照了一个Integer它的值是i ?” – 即使当I为空时,也可以毫无困难地回答这个问题。 不幸的是,Java并不直接检查不同types的操作数是否相等; 相反,它会检查语言是否允许将任一操作数的types转换为另一种操作数的types,如果是,则将转换的操作数与未转换的操作数进行比较。 这种行为意味着对于具有某些types组合的variablesxyz ,可能有x==yy==z但是x!=z [例如x = 16777216f y = 16777216 z = 16777217]。 这也意味着比较I==i被翻译为“将我转换为一个int ,如果不会引发exception,将它比较i

这是因为Java的自动装箱function。 编译器检测到,在比较的右侧,您使用的是原始整数,并且需要将包装器的整数值解包为原始的int值。

由于这是不可能的(因为你排队了) NullPointerException被抛出。

i == 0 Java将尝试做自动拆箱,并做一个数值比较(即“是存储在包装对象存储在i引用的值为0 ?”)。

由于inull的拆箱将抛出一个NullPointerException

推理是这样的:

JLS第15.21.1节数值相等运算符==和!=的第一句如下所示:

如果等于运算符的操作数都是数字types的,或者一个是数字types,另一个是可转换的(第5.1.8节)为数字types,则对操作数执行二进制数字升级(第5.6.2节)。

显然, i可以转换为数字types, 0是一个数字types,所以二进制数字提升是在操作数上执行的。

§5.6.2二进制数字促销 (除其他外):

如果任何操作数是引用types,则执行拆箱转换(§5.1.8)。

§5.1.8拆箱转换说(除其他外):

如果r为null,则取消装箱转换将引发NullPointerException