String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
JLS 5,第15.18.1.1节 JLS 8 § 15.18.1 “字符串连接运算符+”,导致JLS 8,§ 5.1.11“字符串转换”,要求此操作成功而不失败:
...现在只需要考虑引用值。 如果引用为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
的 append 方法可以很好地处理 null,因为在这种情况下第一个参数是 null
,所以会调用 String.valueOf()
方法,因为 StringBuilder 没有接受任意引用类型的构造函数。
如果你使用 s = "hello" + s
,等效的代码将会是:
s = new StringBuilder("hello").append(s).toString();
在这种情况下,append
方法会将 null
传递给 String.valueOf()
进行委托。
注意:字符串拼接实际上是编译器可以决定执行哪些优化的少数几个地方之一。因此,“完全等效”的代码可能因编译器而异。这种优化是允许的,根据JLS, Section 15.18.1.2:
为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似的技术来减少通过求值表达式而创建的中间String对象的数量。
我用于确定上面“等效代码”的编译器是Eclipse的编译器ecj。
StringBuilder
的任何一个 append
方法(如 append(String)
或 append(CharSequence)
)而非 append(Object)
,则StringBuilder
在技术上不会使用String.valueOf
,尽管无论如何,将 null
改为 "null"
。 - ColinDnull
应该抛出NPE。我认为打印null
就像一种“元”行为,例如调试时,当我想看看变量里面有什么时,但默认的“非元”行为应该是抛出NPE。 - aliopilogger.print("doing something to " + obj);
)。现在我们有比字符串连接更好的日志组合方式,但这在1995年并不一定成立。 - Mark Peterss = (new StringBuilder()).append((String)null).append("hello").toString();
追加方法可以处理null
参数。
你没有使用 "null",因此你不会得到异常。如果你想要 NullPointer 异常,只需执行以下操作:
String s = null;
s = s.toString() + "hello";
我认为你想做的是:
String s = "";
s = s + "hello";
String.valueOf(Object)
方法中指定的行为。当您进行连接操作时,使用valueOf
来获取String
表示形式。如果对象是null
,则有一个特殊情况,此时使用字符串"null"
。
public static String valueOf(Object obj)
返回Object参数的字符串表示形式。
参数: obj - 一个Object。
返回值:
如果参数为空,则返回等于"null"的字符串;否则,返回obj.toString()的值。
+运算符
(例如,请参见Jonathan的回答)。在s + "hello"
这一行中没有方法调用,因此不存在NPE的可能性,因为没有对象接收器(并且'代码转换'必须遵守此协议)。祝你编程愉快。 - user166390nullhello
仍然是一种反直觉、无用的行为。 - aliopiString
值时,它是+
运算符的定义。最终,除非操作数是不同类型的,否则它不会尝试在任何操作数上调用方法,此时将调用隐式的toString
。 显然,+
运算符的意图是简化值的显示,而不以任何方式引用结果长度等。 - tishma