为什么将 Integer 与 int 进行比较可以在 Java 中抛出 NullPointerException?

2022-08-31 11:50:58

观察这种情况让我感到非常困惑:

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尝试从中提取int值)并且比较操作的优先级较低,因此会引发异常。null

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

目前,当使用包装的基元抛出并且未使用真正的对象类型抛出时,它看起来不一致。NullPointerException


答案 1

简短的回答

关键点是:

  • ==两个引用类型之间始终是引用比较
    • 通常情况下,例如,使用 和 ,您希望改用IntegerStringequals
  • ==在引用类型和数字基元类型之间始终是数字比较
    • 引用类型将进行拆箱转换
    • 拆箱总是抛出nullNullPointerException
  • 虽然Java有许多特殊处理,但它实际上不是一个原始类型。String

上述语句适用于任何给定的有效 Java 代码。有了这种理解,您呈现的代码段中没有任何不一致之处。


长答案

以下是相关的JLS部分:

JLS 15.21.3 参考相等运算符 ==!=

如果相等运算符的操作数既是引用类型又是 null 类型,则该操作是对象相等。

这解释了以下内容:

Integer i = null;
String str = null;

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

两个操作数都是引用类型,这就是引用相等比较的原因。==

这还解释了以下内容:

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

要实现数值相等,至少一个操作数必须是数值类型==

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

如果相等运算符的操作数都是数值类型,或者一个是数值类型,另一个是可转换为数值类型,则对操作数执行二进制数值提升。如果操作数的提升类型为 或 ,则执行整数相等性检验;如果提升的类型为双精度',则执行浮点相等性检验。intlongfloat or

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

这解释了:

Integer i = null;

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

以下是 Effective Java 2nd Edition, Item 49: Prefer primitives 而不是 boxed primitives 的摘录

总之,只要您有选择,就应优先使用带框的基元。基元类型更简单、更快捷。如果必须使用盒装基元,请小心!自动装箱可减少使用装箱基元的详细程度,但不会减少危险。当您的程序将两个盒装基元与运算符进行比较时,它会进行标识比较,这几乎肯定不是您想要的。当您的程序执行涉及盒装和未装箱基元的混合类型计算时,它会取消装箱,而当您的程序执行拆箱时,它可以抛出 .最后,当您的程序框住基元值时,可能会导致昂贵且不必要的对象创建。==NullPointerException

有些地方你别无选择,只能使用盒装基元,例如泛型,但除此之外,你应该认真考虑使用盒装基元的决定是否合理。

引用

相关问题

相关问题


答案 2

您的 NPE 示例等效于以下代码,这要归功于自动装箱

if ( i.intValue( ) == 0 )

因此,NPE if 是 。inull


推荐