本文转载自微信公众号《程序新视界》,作者为二哥。转载本文请联系程序新视界公众号。前言String类型真是一个神奇的存在,动不动就会出现一些让人迷惑的错误。今天看到一篇文章提到当String的值为null时,进行字符串相加拼接,null会被视为字符串拼接。例如下面的代码:Strings=null;s=s+"hello";System.out.println(s+"world");你期望的结果可能是“helloworld”,但实际结果是“nullhelloworld”,很神奇。其实这也没什么,练习一下就可以看到结果了。但是当你的好奇心开始在网上搜索原因时,你看到的答案可能是错误的。我搜索的时候看到几万次的文章解释错误。为了排除一些误导,特为大家分析原因。错误原因分析如果搜索上面的问题,可能看到的答案是:s+"world"等价于s=String.valueOf(s)+"word";然后附带valueOf方法:publicstaticStringvalueOf(Objectobj){ return(obj==null)?"null":obj.toString();}你信吗?如果你相信它,你可能就错了。下面分析一下为什么会错。Java编译器的优化我们知道,当我们编写如下代码时,Java编译器会为我们做一些优化:Stringa="Hello";Stringb="World";System.out.println(a+b);如何优化上述代码经过编译器优化后,相当于:StringBuildersb=newStringBuilder();sb.append("Hello");sb.append("World");Stringresult=sb.toString();System.out.println(结果);也就是说,plus操作会基于StringBuilder操作进行优化,而不是上面提到的String.valueOf操作。那么,上面为null的情况相当于下面的操作:StringBuildersb=newStringBuilder(null);sb.append("hello");sb.append("world");Stringresult=sb.toString();System.out.println(结果);至此,我们再看一下StringBuilder(null)构造方法的底层实现,最后转入其父类AbstractStringBuilder中的append方法:publicAbstractStringBuilderappend(Stringstr){if(str==null)returnappendNull();intlen=str.length();ensureCapacityInternal(count+len);str.getChars(0,len,value,count);count+=len;returnthis;}对应的appendNull方法实现为:privateAbstractStringBuilderappendNull(){intc=count;ensureCapacityInternal(c+4);finalchar[]value=this.value;value[c++]='n';value[c++]='u';value[c++]='l';value[c++]='l';count=c;returnthis;}在appendNull方法中,null被视为字符串“null”。这就是拼接中出现null的原因。字节码跟踪对于上面的例子,如果想看看编译器是如何处理的,可以使用javap-c命令查看对应的字节码:从字节码可以看出,与上面的分析基本一致。所以,信书不如无书。扩展问题解决了上述问题。我们再来看看。如果我们简单的打印null,它是如何输出的呢?字符串=空;System.out.println(s);执行上面的程序,控制台打印null。这个null从哪里来?关于什么?直接看println的底层实现:publicvoidprint(Strings){if(s==null){s="null";}write(s);}最后调用print方法,如果为null,则打印空字符串。支持,原来的valueOf方法还没有出现,那么在什么场景下会用到valueOf方法呢?当对象为Object类型时:Objects=null;Strings1=String.valueOf(s);System.out.println(s1);也就是说,显式调用valueOf方法时,此时s1的值直接为空字符串。展开一下,对于一些基本类型的封装类,如Integer、Double等:Integeri=null;System.out.println(i);上面代码的处理不一样,println方法的实现如下:publicvoidprintln(Objectx){Strings=String.valueOf(x);synchronized(this){print(s);newLine();}}也就是说,先在对应的Object对象上调用valueOf,返回上面的例子,如果Object为null,则该方法返回一个null字符串,后续打印机直接为null。总结字符串拼接是一个很常见的问题。一不小心null就会被拼接。而这种情况的出现涉及到Java编译器的优化。是不是很有趣?而且正如开头所说,我们在网上搜索资料的时候,也要辩证的看答案的准确性。
