public class Test {
public static void main(String[] args) {
String s = null;
String s1 = null;
Integer i = null;
Integer i1 = null;
System.out.println(s+i);
System.out.println(i+s);
System.out.println(s+s1);
try {
System.out.println(i+i1);
} catch (NullPointerException np) {
System.out.print("NullPointerException");
}
}
}
问题很简单 - 为什么我只在最后一行收到NullPointerException
?
答案 0 :(得分:60)
您的代码使用了两个不同的additive operators。前三行使用string concatenation,而最后一行使用numeric addition。
字符串连接是well-defined to turn null
into "null"
:
- 如果引用为
null
,则会将其转换为字符串"null"
(四个ASCII字符n
,u
,l
,{{1} })。
因此没有NPE。
将两个l
个对象添加到一起需要它们为unboxed。这会导致Integer
引用被取消引用,从而导致NPE:
- 如果
null
为空,则取消装箱转化将引发r
答案 1 :(得分:17)
请注意,前三个+
运算符用法涉及字符串连接。只有最后一个是实际数字总和。当涉及字符串连接时(涉及s
变量),Java编译器使用一些聪明的技巧来提高性能。它用+
替换StringBuilder
运算符。例如,您的第一行被翻译为:
StringBuilder tmp = new StringBuilder();
tmp.append(s);
tmp.append(i);
System.out.println(tmp);
StringBuilder
是null
- 友好,所以无论你作为一个参数传递什么,它都很好地用"null"
字符串替换它。
最后一行的情况有所不同。在那里你引用了两个Integer
个对象。 JVM在这里唯一能做的就是取消它们(i.intValue()
)并进行实际计算。取消装箱null
会导致NullPointerException
。
答案 2 :(得分:5)
+
的任何内容的连接(String
运算符)都会产生String
。每个操作数首先转换为字符串(使用toString()
或使用值"null"
),然后连接。
但最后一项操作仅涉及Integer
,因此之前的规则不适用。它不是连接,而是添加。但是要添加两个Integer
,它会将对象(Integer
s)转换为原始值(int),如果Integer
为空,则不可能。这就是你得到NullPointerException
的原因。
答案 3 :(得分:1)
似乎是自动拆箱的情况。如果您有两个Integer
first
和second
,则其添加结果将为first.intValue() + second.intValue()
。由于它们都是null,因此导致NPE。
答案 4 :(得分:0)
查看程序的字节码。您会注意到您的null对象作为参数传递给PrintStream.print()方法。 print()方法的源代码使用String.valueOf(),如下所示:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
答案 5 :(得分:0)
应该注意的是,从输出中可以明显看出这个问题的答案:
C:\Temp>java Test
nullnull
nullnull
nullnull
NullPointerException