空闲时间,一个好友发过来一段代码很有意思,运行的结果比较诡异,代码很简单,三两行,详细如下:
StringBuilder sb = new StringBuilder();
String str = sb.append("a").toString();
System.out.println(str.intern() == str);
执行代码,输出结果为false,还可以接受,不过再换下面这段代码
StringBuilder sb = new StringBuilder();
String str = sb.append("a").append("b").toString();
System.out.println(str.intern() == str);
执行代码,输出结果为true。这就诡异了,为什么只是多调用了一个StringBuilder.append,返回结果竟然不一样呢?
翻看了下StringBuilder.append的源码,没什么特殊的地方,没有什么对象引用的处理呀。那么真相只有一个,就是String.intern导致的了,借用柯南的一句名言:抛开所有不可能的,剩下的,不管多么匪夷所思,那都是事实。
查了下String.intern的文章,有一篇写的非常好,原文如下:https://blog.csdn.net/SEU_Calvin/article/details/52291082
读了这篇文章后,这个问题就比较合理了。
原来,jdk1.7后String.intern做了一些优化处理,拿第一个例子来说,String str = sb.append("a").toString();
这个时候,常量池里存在一个字符串"a"对象,而堆上也存在一个"a"对象str,这里str.intern返回的是常量池的对象,和堆上的str引用不同,返回false,可以解释。
再看上面的诡异的第二个例子 String str = sb.append("a").append("b").toString();这个时候,常量池存在的是"a"对象和"b"对象,但是不存在字符串"ab"对象,堆上存在值为"ab"的str对象。这时候调用str.intern,在jdk1.7之后,常量池里的"ab"对象存的是堆上"ab"字符串的引用,所以str.intern和str其实是一个引用了,==相比返回的true。
这个问题咋看还是很诡异的,String.intern也一直是个比较诡异的方法,感兴趣的可以了解一下,不要在工作中这么比较哈。