【问题标题】:Concatenating null strings in Java [duplicate]在Java中连接空字符串[重复]
【发布时间】:2011-05-14 17:28:15
【问题描述】:

为什么以下工作?我希望NullPointerException 被抛出。

String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"

【问题讨论】:

  • s 的 type 在编译时是已知的,+ operator 在 String 类型上重载(例如,参见 Jonathans 的回答)。 s + "hello" 行中没有方法调用,因此 NPE 没有机会,因为没有对象接收器(并且“代码转换”必须遵守此合同)。编码愉快。
  • 我同意你的思路yavoh。自动字符串化 null 不是 Java 的一个好部分。为 Sun 做了这么容易出错的事情而感到羞耻。
  • @user166390 很好的解释,不是吗,但打印 nullhello 仍然是 IMO 的一种违反直觉、无用的行为。
  • 我想很多开发者都有误解,如果我使用一个空对象,它就会崩溃。事实上,只有当你调用空对象的属性或方法时,才会崩溃。
  • 如果明智的人将 null 转换为空字符串 "" 我有点理解,但转换为文字 4 字符字符串 "null"?!

标签: java string concatenation string-concatenation


【解决方案1】:

为什么必须它起作用?

JLS 5, Section 15.18.1.1JLS 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 的 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 类或类似技术来减少通过计算表达式创建的中间字符串对象的数量。

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

【讨论】:

  • StringBuilder 在技术上不使用 String.valueOf 如果任何 append 方法以外的 append(Object)(例如 append(String)append(CharSequence))被调用,虽然 null 被更改为"null" 无论如何。
  • 谢谢@ColinD,在这种情况下我错了,因为没有构造器StringBuilder(Object)。我已经编辑了我的帖子以直接反映字节码。
  • 对行为异常错误的更多解释。它应该打印hello
  • @Adowrath 我改变了主意,你是对的,访问null 应该会引发 NPE。我认为打印 null 类似于 "meta" 行为,例如在调试时,当我想查看该变量内部的内容时,但默认的 "non-meta" 行为应该是抛出 NPE。
  • @aliopi:我认为他们在这里做出了一个合理的决定,使串联为空安全。这可能违反了最小意外原则,但他们可能试图避免过多的 NPE 或日志和诊断代码中的显式空检查(例如logger.print("doing something to " + obj);)现在我们有比字符串连接更好的日志组合,但那是1995 年不一定正确。
【解决方案2】:

请参阅 Java 语言规范的 5.415.18 部分:

字符串转换仅适用于 二元 + 运算符的操作数 when 参数之一是字符串。在 这个单一的特殊情况,另一个 + 的参数被转换为 字符串,以及一个新的字符串,它是 两个字符串的连接是 + 的结果。字符串转换 内详细说明 字符串的描述 连接 + 运算符。

如果只有一个操作数表达式是 输入String,则字符串转换为 在另一个操作数上执行 在运行时生成一个字符串。这 结果是对字符串的引用 对象(新创建的,除非 表达式是编译时常量 表达式(§15.28))即 两个操作数的连接 字符串。的字符 左操作数在 右手操作数的字符 在新创建的字符串中。如果 String 类型的操作数为 null,则 使用字符串“null”代替 那个操作数。

【讨论】:

  • 是的!最后是一个带有一些漂亮引号的答案,而无需跳过“StringBuilder”(这是编译器可能会或可能不会执行的优化)。
【解决方案3】:

您没有使用“null”,因此您没有收到异常。如果你想要 NullPointer,就这样做

String s = null;
s = s.toString() + "hello";

我认为你想做的是:

String s = "";
s = s + "hello";

【讨论】:

  • 你正在使用 null,你只是不想取消引用它。
【解决方案4】:

这是在 Java API 的 String.valueOf(Object) 方法中指定的行为。当您进行连接时,valueOf 用于获取 String 表示。如果 Object 是null,则有一种特殊情况,在这种情况下使用字符串"null"

public static String valueOf(Object obj)

返回 Object 参数的字符串表示形式。

参数: obj - 一个对象。

返回:

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

【讨论】:

    【解决方案5】:

    第二行转化为如下代码:

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

    append 方法可以处理null 参数。

    【讨论】:

      猜你喜欢
      • 2015-07-31
      • 1970-01-01
      • 1970-01-01
      • 2012-02-02
      • 2019-01-16
      • 1970-01-01
      • 2014-07-23
      • 2014-05-20
      • 2016-06-29
      相关资源
      最近更新 更多