在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
在我看来仍然是一个反直觉的、无用的行为