在 Java 中连接空字符串
为什么以下工作?我期望被扔掉。NullPointerException
String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
为什么以下工作?我期望被扔掉。NullPointerException
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
自己执行此操作)
所有追加方法都处理 null 就好了。在这种情况下,因为 是第一个参数,而是被调用,因为 StringBuilder 没有采用任何任意引用类型的构造函数。StringBuilder
null
String.valueOf()
如果您要这样做,则等效代码将是:s = "hello" + s
s = new StringBuilder("hello").append(s).toString();
在这种情况下,append 方法采用 null,然后将其委托给 。String.valueOf()
注意:字符串串联实际上是编译器决定执行哪些优化的罕见位置之一。因此,“完全等效”代码可能因编译器而异。JLS 第 15.18.1.2 节允许进行此优化:
为了提高重复字符串串联的性能,Java 编译器可以使用 StringBuffer 类或类似技术来减少通过计算表达式创建的中间 String 对象的数量。
我用来确定上面“等效代码”的编译器是Eclipse的编译器ecj。
请参阅 Java 语言规范的第 5.4 节和第 15.18 节:
当其中一个参数是 String 时,字符串转换仅适用于二进制 + 运算符的操作数。在这种单一特殊情况下,+ 的另一个参数被转换为字符串,而作为两个字符串串联的新字符串是+的结果。字符串转换在字符串串联 + 运算符的说明中详细指定。
和
如果只有一个操作数表达式是 String 类型,则在另一个操作数上执行字符串转换,以便在运行时生成字符串。结果是对 String 对象(新创建的,除非表达式是编译时常量表达式 (§15.28))的引用,该对象是两个操作数字符串的串联。在新创建的字符串中,左侧操作数的字符位于右侧操作数的字符之前。如果 String 类型的操作数为 null,则使用字符串“null”而不是该操作数。