前言
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。
小结
字符串拼接是很常见的问题,一不小心会出现将null给拼接上的情况。而这状况的出现又牵扯到Java编译器的优化,是不是很有意思?而且正如最开始所述,当我们在网络上搜索资料时也要辨证的去看待答案的准确性。
面试系列
- 《面试题:聊聊TCP的粘包、拆包以及解决方案》
- 《面试题:重写equals方法为什么通常会重写hashcode方法?》
- 《面试官:如何找出字符串中无重复最长子串?》
- 《还不懂Java的泛型?只用这一篇文章,保证你面试对答如流》
- 《面试题:将字符串反转的8种方法,你能想到几种?》
本文链接:http://m.zhangshiyu.com/post/21788.html
- 「亲手逼死儿子后,男人悔不当初」后续全文免费阅读_[傅司衍轩轩佳佳]最新章节免费阅读
- 我老婆是东晋第一女魔头反转剧情碎片化试读_「唐禹谢秋瞳」反转剧情碎片化试读
- [儿子在水里窒息挣扎时,游泳冠军的老公在和白月光选泳裤]独家章节限时试读_「老公乐乐陆沉」节选试读
- 「百倍返还:从直播间开始逆袭封神!」人气小说未删减节选_「张涛李淼淼」限免完整章节合集
search zhannei
最新文章
-
- 全文缘来是你早注定良心(萧千雅胡一阳)列表_全文缘来是你早注定良心
- 「爱让我溺毙而亡」小说节选推荐_[羽柔乔羽柔林絮]章节限时抢先看
- 此去经年人未还霍沉洲沈青禾后续结局霍沉洲
- 山海不相逢内容精选(温逸尘沈衿)_山海不相逢内容精选(温逸尘沈衿)
- (番外)+(全书)霍沉洲沈青禾此去经年人未还(霍沉洲沈青禾)_(霍沉洲沈青禾此去经年人未还)列表_笔趣阁(霍沉洲沈青禾)
- (番外)+(全书)霍沉洲沈青禾(此去经年人未还霍沉洲沈青禾)完结_(霍沉洲沈青禾)列表_笔趣阁(此去经年人未还霍沉洲沈青禾)
- 「重回八零,拒绝替嫁冲喜」章节彩蛋限时释出_卫东玉兰苏夏人气小说未删减节选
- 重生七零祁同伟不再是农民儿子结局+番外纯净版全书免费重生七零祁同伟不再是农民儿子结局+番外纯净版全书免费
- 傅雅宁的神女老婆,却在背地承欢作乐顾尘傅雅宁全书在线
- 全文神女老婆,却在背地承欢作乐全局(顾尘傅雅宁)列表_全文神女老婆,却在背地承欢作乐全局
- (番外)+(全书)此去经年人未还全书+番外+后续免费下载_(沈青禾霍沉洲)此去经年人未还全书+番外+后续列表_笔趣阁(沈青禾霍沉洲)
- 完结文毁容的姐姐和瞎眼的我离开后,姜家两兄弟悔哭了+后续列表_完结文毁容的姐姐和瞎眼的我离开后,姜家两兄弟悔哭了+后续(林梦婉)
Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1