在Java中连接空字符串

在Java中连接空字符串,java,string,concatenation,string-concatenation,Java,String,Concatenation,String Concatenation,为什么下面的工作?我希望抛出NullPointerException String s = null; s = s + "hello"; System.out.println(s); // prints "nullhello" 为什么它必须工作? 导致的操作要求此操作成功而不失败: …现在只需要考虑参考值如果引用为null,则将其转换为字符串“null”(四个ASCII字符n、u、l、l)。否则,转换就好像是通过调用引用对象的toString方法(不带参数)来执行的;但是如果调用toStrin

为什么下面的工作?我希望抛出
NullPointerException

String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
为什么它必须工作? 导致的操作要求此操作成功而不失败:

…现在只需要考虑参考值如果引用为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"
(您可以通过使用自己进行此操作)

StringBuilder
的append方法可以很好地处理null。在这种情况下,由于
null
是第一个参数,因此将调用
String.valueOf()
,因为StringBuilder没有采用任何任意引用类型的构造函数

如果改为执行
s=“hello”+s
,则等效代码为:

s = new StringBuilder("hello").append(s).toString();
在这种情况下,append方法接受null,然后将其委托给
String.valueOf()

注意:字符串串联实际上是编译器决定要执行哪些优化的极少数地方之一。因此,“完全等效”的代码可能因编译器而异。此优化可通过以下方式实现:

为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似技术来减少通过表达式求值创建的中间字符串对象的数量


我用来确定上述“等效代码”的编译器是Eclipse的编译器,ecj

第二行被转换为以下代码:

s = (new StringBuilder()).append((String)null).append("hello").toString();

append方法可以处理
null
参数。

这是Java API方法中指定的行为。在进行串联时,将使用
valueOf
获取
字符串
表示形式。如果对象为
null
,则有一种特殊情况,在这种情况下,将使用字符串
“null”

publicstaticstringvalueof(objectobj)

返回对象参数的字符串表示形式

参数: obj——一个物体

返回:

如果参数为null,则为等于“null”的字符串;否则,将返回obj.toString()的值

您没有使用“null”,因此不会得到异常。如果你想要空指针,就这么做吧

String s = null;
s = s.toString() + "hello";
我想你想做的是:

String s = "";
s = s + "hello";
请参阅Java语言规范的第节和第节:

字符串转换仅适用于 二元+运算符的操作数 其中一个参数是字符串。在里面 这是一个特例,另一个 +的参数转换为 字符串,以及一个新字符串,它是 两个字符串的串联是不正确的 计算结果为+。字符串转换 在 字符串的描述 串联+运算符

如果只有一个操作数表达式为 输入String,然后进行字符串转换 对要删除的另一个操作数执行 在运行时生成字符串。这个 结果是对字符串的引用 对象(新创建的,除非 表达式是编译时常量 表达式(§15.28)),即 两个操作数的并置 串。这本书的特点 左操作数位于 右操作数的字符 在新创建的字符串中。如果 字符串类型的操作数为null,则 使用字符串“null”代替 那个操作数


StringBuilder
从技术上讲不使用
String.valueOf
如果有
append
方法而不是
append(Object)
(例如
append(String)
append(CharSequence)
)被调用,尽管
null
被更改为
“null”
,但不管怎样。谢谢@ColinD,在本例中,我错了,因为没有构造函数
StringBuilder(Object)
。我已经编辑了我的帖子,直接反映了字节码。更多关于行为意外错误的解释。它应该打印
hello
@Adowrath我改变主意了,你是对的,访问
null
应该抛出一个NPE。我认为打印
null
类似于一种“元”行为,例如在调试时,当我想查看该变量中的内容时,但默认的“非元”行为应该是抛出一个NPE。@aliopi:我认为他们在这里做出了一个合理的决定,使串联空值安全。这可能违反了最小意外原则,但他们可能试图避免日志和诊断代码(例如,
logger.print(“对“+obj做点什么”);
)中出现过多的NPE或显式空检查。如今,我们的日志合成比字符串串联更好,但这在1995年并不一定是真的。是的!最后是一个带有一些漂亮引号的答案,而不必跳转到“StringBuilder”(这是编译器可能执行或可能不执行的优化)。s的类型在编译时是已知的,
+运算符
在字符串类型上重载(例如,请参阅Jonathans答案)。在
s+“hello”
行中没有方法调用,因此没有机会进行NPE,因为没有对象接收器(并且“代码转换”必须遵守此合同)。快乐的编码。我同意你的想法。自动字符串化null不是Java的一个好部分。Sun做了这么容易出现bug的事情,真让人感到羞耻。@user166390这是一个很好的解释,不是吗,但是打印
nullhello
在我看来仍然是一个反直觉的、无用的行为