为什么比较整数与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?
目前,当NullPointerException
exception抛出时,它看起来不一致,并且不会抛出真正的对象types。
简答题
关键是这样的:
-
==
两个参考types之间总是参考比较- 多数情况下,例如使用
Integer
和String
,你会想要使用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的操作数是
int
或long
,则执行整数相等性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。
- “取消装箱转换将
Integer
types转换为int
types” - “如果
r
为null
,拆箱转换会抛出一个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 )
因此,如果i
是null
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组合的variablesx
, y
和z
,可能有x==y
和y==z
但是x!=z
[例如x = 16777216f y = 16777216 z = 16777217]。 这也意味着比较I==i
被翻译为“将我转换为一个int
,如果不会引发exception,将它比较i
。
这是因为Java的自动装箱function。 编译器检测到,在比较的右侧,您使用的是原始整数,并且需要将包装器的整数值解包为原始的int值。
由于这是不可能的(因为你排队了) NullPointerException
被抛出。
在i == 0
Java将尝试做自动拆箱,并做一个数值比较(即“是存储在包装对象存储在i
引用的值为0
?”)。
由于i
是null
的拆箱将抛出一个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