为什么以下工作?我希望抛出NullPointerException
。
String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
答案 0 :(得分:174)
导致JLS 5, Section 15.18.1.1的JLS 8 § 15.18.1 "String Concatenation Operator +" JLS 8, § 5.1.11 "String Conversion"要求此操作成功而不会失败:
...现在只需要考虑参考值。 如果引用为null,则将其转换为字符串“null”(四个ASCII字符n,u,l,l)。否则,执行转换就好像通过调用没有参数的引用对象的toString方法一样;但是如果调用toString方法的结果为null,则使用字符串“null”。
让我们看一下字节码!编译器接受您的代码:
String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
并将其编译为字节码,就像你写了这样:
String s = null;
s = new StringBuilder(String.valueOf(s)).append("hello").toString();
System.out.println(s); // prints "nullhello"
(您可以使用javap -c
)
StringBuilder
的追加方法都处理null就好了。在这种情况下,因为null
是第一个参数,所以调用String.valueOf()
,因为StringBuilder没有采用任意引用类型的构造函数。
如果您要完成s = "hello" + s
,则等效代码为:
s = new StringBuilder("hello").append(s).toString();
在这种情况下,append方法取空,然后然后将其委托给String.valueOf()
。
注意:字符串连接实际上是编译器决定执行哪些优化的罕见地方之一。因此,“完全等效”代码可能因编译器而异。 JLS, Section 15.18.1.2允许此优化:
为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似技术来减少通过表达式求值创建的中间String对象的数量。
我用来确定上面“等效代码”的编译器是Eclipse的编译器, ecj 。
答案 1 :(得分:23)
字符串转换仅适用于 二进制+运算符的操作数 其中一个参数是String。在 这个单一的特例,另一个 +的参数转换为a 字符串,以及一个新的String 这两个字符串的连接是 +的结果。字符串转换 在...中详细说明 字符串的描述 连接+运算符。
和
如果只有一个操作数表达式 键入String,然后进行字符串转换 在另一个操作数上执行 在运行时生成一个字符串。该 result是对String的引用 对象(新创建的,除非 expression是一个编译时常量 表达式(§15.28))即 两个操作数的串联 字符串。的人物 左手操作数先于 右手操作数的字符 在新创建的字符串中。如果 那么String类型的操作数是null 使用字符串“null”代替 那个操作数。
答案 2 :(得分:11)
第二行转换为以下代码:
s = (new StringBuilder()).append((String)null).append("hello").toString();
append方法可以处理null
个参数。
答案 3 :(得分:10)
您没有使用“null”,因此您没有获得异常。如果您想要NullPointer,只需执行
String s = null;
s = s.toString() + "hello";
我认为你想要做的是:
String s = "";
s = s + "hello";
答案 4 :(得分:7)
这是Java API的String.valueOf(Object)
方法中指定的行为。进行连接时,valueOf
用于获取String
表示。如果对象是null
,则会有一种特殊情况,在这种情况下使用字符串"null"
。
public static String valueOf(Object obj)
返回Object参数的字符串表示形式。
参数: obj - 一个对象。
返回:
如果参数为null,则字符串等于“null”;否则,返回obj.toString()的值。