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

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

[[398920]]

本文转载自微信公众号「程序新视界」,作者二师兄 。转载本文请联系程序新视界公众号。

前言

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

比如下面这段代码:

  1. String s = null
  2. s = s + "hello"
  3. System.out.println(s + " world"); 

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

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

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

错误的原因分析

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

  1. s + " world" 等价于 s = String.valueOf(s)+"word"

然后附带valueOf方法:

  1. public static String valueOf(Object obj) { 
  2.   return (obj == null) ? "null" : obj.toString(); 

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

Java编译器的优化

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

  1. String a = "Hello "
  2. String b = "World"
  3. System.out.println(a + b); 

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

  1. StringBuilder sb = new StringBuilder(); 
  2. sb.append("Hello "); 
  3. sb.append("World"); 
  4. String result = sb.toString(); 
  5. System.out.println(result); 

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

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

  1. StringBuilder sb = new StringBuilder(null); 
  2. sb.append("hello"); 
  3. sb.append(" world"); 
  4. String result = sb.toString(); 
  5. System.out.println(result); 

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

  1. public AbstractStringBuilder append(String str) { 
  2.     if (str == null
  3.         return appendNull(); 
  4.     int len = str.length(); 
  5.     ensureCapacityInternal(count + len); 
  6.     str.getChars(0, len, value, count); 
  7.     count += len; 
  8.     return this; 

对应的appendNull方法实现为:

  1. private AbstractStringBuilder appendNull() { 
  2.     int c = count
  3.     ensureCapacityInternal(c + 4); 
  4.     final char[] value = this.value; 
  5.     value[c++] = 'n'
  6.     value[c++] = 'u'
  7.     value[c++] = 'l'
  8.     value[c++] = 'l'
  9.     count = c; 
  10.     return this; 

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

字节码追踪

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

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

拓展问题

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

  1. String s = null
  2. System.out.println(s); 

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

  1. public void print(String s) { 
  2.     if (s == null) { 
  3.         s = "null"
  4.     } 
  5.     write(s); 

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

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

  1. Object s = null
  2. String s1 = String.valueOf(s); 
  3. System.out.println(s1); 

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

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

  1. Integer i = null
  2. System.out.println(i); 

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

  1. public void println(Object x) { 
  2.     String s = String.valueOf(x); 
  3.     synchronized (this) { 
  4.         print(s); 
  5.         newLine(); 
  6.     } 

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

小结

 

字符串拼接是很常见的问题,一不小心会出现将null给拼接上的情况。而这状况的出现又牵扯到Java编译器的优化,是不是很有意思?而且正如最开始所述,当我们在网络上搜索资料时也要辨证的去看待答案的准确性。

 

责任编辑:武晓燕 来源: 程序新视界
相关推荐

2018-04-04 14:41:35

前端JavaScript面试

2022-03-18 09:42:54

JavaString

2024-03-15 09:50:00

NULLSQL优化

2016-03-08 09:50:42

2021-05-17 10:27:42

地址虚拟逻辑

2017-02-15 20:32:42

2018-11-14 14:27:00

iOS 12Siri苹果

2017-06-15 08:52:36

数据分析可视化软件开发

2020-08-26 14:00:37

C++string语言

2016-03-28 09:39:54

2011-11-22 09:17:48

程序员

2022-05-05 19:00:22

PythonPython特性

2023-03-21 23:48:31

ChatGPT漏洞安全

2012-05-07 17:11:54

Galaxy

2017-02-10 16:39:47

戴尔商用电脑促销

2019-06-03 13:53:51

苹果iOS 13MacOS 13

2021-09-14 09:19:49

一号多卡手机卡号码

2015-09-15 17:44:04

桌面智真DP300华为

2019-06-18 09:28:34

iOS 13手机黑暗模式

2018-11-28 11:20:09

区块链中本聪分布式
点赞
收藏

51CTO技术栈公众号