写点什么

String 拼接出现 null?你看到的分析可是错的

用户头像
Java王路飞
关注
发布于: 2021 年 05 月 12 日
String拼接出现null?你看到的分析可是错的

前言

String 类型真是个神奇的存在,动不动就会出现一些迷惑人的错误。今天看到一篇文中提到当 String 的值为 null 时,进行字符串相加拼接,会出现把 null 当做字符串拼接的现象。

比如下面这段代码:

String s = null;s = s + "hello";System.out.println(s + " world");
复制代码

你预期的结果可能是“hello world”,但实际的结果是“nullhello world”,神奇吧。

其实这倒没什么,实践一下就可以看到结果。但当你好奇心作祟,在网上搜为什么时,你看到的答案可能是错的。

我在搜索时,看到访问量上万的文章给出的解释竟然错误的。为了排除一些误导,特意为大家分析一下原因。

错误的原因分析

如果对上述问题进行搜索,你可能看到的答案是:

s + " world" 等价于 s = String.valueOf(s)+"word";
复制代码

然后附带 valueOf 方法:

public static String valueOf(Object obj) {  return (obj == null) ? "null" : obj.toString();}
复制代码

你信了吗?如果信了可能真的就错了。下面我们就来分析分析为什么错了。

Java 编译器的优化

我们知道,当我们写下面的代码时 Java 编译器会为我们做一些优化:

String a = "Hello ";String b = "World";System.out.println(a + b);
复制代码

如何优化的?上面这段代码经过编译器优化之后,等价于:

StringBuilder sb = new StringBuilder();sb.append("Hello ");sb.append("World");String result = sb.toString();System.out.println(result);
复制代码

也就是说,加号操作会被优化基于 StringBuilder 的操作,而并不是上面提到的 String.valueOf 操作。

那么,上面为 null 的情况也就等价于下面的操作了:

StringBuilder sb = new StringBuilder(null);sb.append("hello");sb.append(" world");String result = sb.toString();System.out.println(result);
复制代码

此时,我们再看一下 StringBuilder(null)这个构造方法的底层实现,最终调到它的父类 AbstractStringBuilder 中的 append 方法:

public AbstractStringBuilder append(String str) {    if (str == null)        return appendNull();    int len = str.length();    ensureCapacityInternal(count + len);    str.getChars(0, len, value, count);    count += len;    return this;}
复制代码

对应的 appendNull 方法实现为:

private AbstractStringBuilder appendNull() {    int c = count;    ensureCapacityInternal(c + 4);    final char[] value = this.value;    value[c++] = 'n';    value[c++] = 'u';    value[c++] = 'l';    value[c++] = 'l';    count = c;    return this;}
复制代码

在 appendNull 方法中就是将 null 当做字符串“null”来处理了。这也就是为什么会在拼接中出现 null 的原因。

字节码追踪

针对上述示例,如果你想看编译器是如何处理的,可以通过 javap -c 命令来查看对应字节码:

通过字节码可以看出,基本上与上面的分析的一致。所以说,尽信书不如无书。

拓展问题

解决了上述问题,再来看看,如果我们单纯地就打印 null 是怎么输出的?

String s = null;System.out.println(s);
复制代码

执行上述程序,控制台打印 null,这个 null 是哪儿来的呢?直接看 println 的底层实现:

public void print(String s) {    if (s == null) {        s = "null";    }    write(s);}
复制代码

最终调用到了 print 方法,如果为 null,则打印 null 字符串。

支持,还没有出现最初的 valueOf 方法,那么 valueOf 方法在什么场景下会用到呢?在对象为 Object 类型时:

Object s = null;String s1 = String.valueOf(s);System.out.println(s1);
复制代码

也就是说在明确调用 valueOf 方法时,此时 s1 的值直接是 null 字符串。

再拓展一下,针对一些基础类型的包装类,比如 Integer、Double 等:

Integer i = null;System.out.println(i);
复制代码

上述代码的处理又不太一样,println 方法实现如下:

public void println(Object x) {    String s = String.valueOf(x);    synchronized (this) {        print(s);        newLine();    }}
复制代码

也就是说先对对应的 Object 对象调用 valueOf,回到上面的示例,如果 Object 为 null,该方法返回 null 字符串,后续打印机直接为 null。

原文链接:https://mp.weixin.qq.com/s?__biz=MzI0NDAzMzIyNQ==&mid=2654071110&idx=1&sn=a67333214499cea9377085d023893341&utm_source=tuicool&utm_medium=referral

如果觉得本文对你有帮助,可以关注一下我公众号,回复关键字【面试】即可得到一份 Java 核心知识点整理与一份面试大礼包!另有更多技术干货文章以及相关资料共享,大家一起学习进步!


发布于: 2021 年 05 月 12 日阅读数: 12
用户头像

Java王路飞

关注

需要资料添加小助理vx:17375779923 即可 2021.01.29 加入

Java领域;架构知识;面试心得;互联网行业最新资讯

评论

发布
暂无评论
String拼接出现null?你看到的分析可是错的