“哥,看了你发的文章,感觉不想学Java了!”三姐怒道。“哪一个?”看着三妹严肃的脸,我关心的问道。“是美团技术团队对String.intern()的深度解析!”三妹回答。https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html“哦,我想起来了,不是好文章,通俗易懂,精华中的精华,看完之后,你应该对String的实习生有个透彻的了解吧。”“很好,就是听不懂!”三妹委屈道:“哥,你怎么不自己给我解释啊?”嗯,你上次学的字符串常量池弄明白了吗?”“嗯。”三姐微微点头,要看懂美团技术团队的这篇文章,只需要记住这几点:第一,用双引号会存放在字符串常量池中,其次,使用new关键字创建的字符串对象,首先会在字符串常量池中查找,如果没有找到,则创建一个,然后在堆中创建一个字符串对象;如果找到,直接在堆字符串对象中创建字符第三,对于没有双引号声明的字符串对象,就像下面代码中的s1:Strings1=newString("二哥")+newString("三姐");如果你把放到字符串常量池中,可以调用intern()方法完成。不过需要注意的是,在Java7中,字符串常量池从永久代移到了堆中,虽然永久代还没有在这个t被完全删除我。在Java8中,永久代被完全移除。这一变化也直接影响了String.intern()方法的执行策略。Java7之前,在执行String.intern()方法时,无论对象是否已经在堆中创建,字符串常量池仍然会创建一个内容完全相同的新对象;Java7之后,由于字符串常量池是放在堆中的,当执行String.intern()方法时,如果对象已经在堆中创建,就不需要在字符串常量池中创建新的对象了,但是直接把对象的引用保存在堆中,这样就节省了一部分内存空间。“三姐,大家猜猜这段代码的输出结果。”我说。Strings1=newString("二哥三姐");Strings2=s1.intern();System.out.println(s1==s2);“大哥,这个我根本猜不出来,你还是直接解释吧。”三姐说道。“好的。”第一行代码会先在字符串常量池中创建一个“二哥三姐”的对象,然后在堆中创建一个“二哥三姐”的对象,s1指的是物体。第二行代码对s1执行intern()方法,会从字符串常量池中检查字符串“二哥三姐”是否存在,此时确实存在,所以s2引用字符串常量池中的对象。说明s1和s2的引用地址不同,一个来自堆,一个来自字符串常量池,所以输出结果为false。“让我们看看手术的结果吧。”我说。false"我画个图帮你理解。"看着三姐吃惊的表情,我耐心的说道。“你现在明白了么?”我问三姐。“是啊,突然明白了!”三姐说道。“好,我们再看下面的代码。”Strings1=newString("二哥")+newString("三姐");Strings2=s1.intern();System.out.println(s1==s2);“会不会也输出false?”三妹有些不确定。“不,这段代码会输出真。”我否定了三妹的猜测。“为什么?”三姐急切地想知道答案。第一行代码会在字符串常量池中创建两个对象,一个是“二哥”,一个是“三姐”,然后会在堆中创建两个匿名对象“二哥”和“三姐”(可以暂时忽略),最后还有一个“二哥三姐”的对象,s1指的是堆中“二哥三姐”的对象。第二行代码在s1上执行intern()方法。该方法会从字符串常量池中检查对象“二哥三姐”是否存在。此时不存在,但是在堆中已经存在了,所以常量池中存放的字符串是“二哥三姐”对象在堆中的引用,也就是引用s2和s1的地址相同,所以输出结果为真。“让我们看看手术的结果吧。”我信心满满的说道。true"我再画一张图帮助你理解。"“哇,我明白了!”三姐松了口气,觉得实习生没什么难懂的。不过需要注意的是,虽然intern可以保证所有相同内容的字符串共享同一个内存空间,但是不要用不好intern,因为任何bufferpool都是有大小限制的,不能无缘无故占用比较稀缺的cache空间,导致没有其他字符串可以占用的坑。另外,字符串常量池本质上是一个固定大小的StringTable。如果放入太多的字符串,会造成严重的哈希冲突,导致链表变长,链表变长意味着字符串常量池的性能会大大降低,因为找一个需要时间一个。“好了,三姐,String的实习生就这些了,这次你明白了吗?”我问。“哥,你真棒!”看着三姐一点一滴的进步,我也感到由衷的高兴。本文转载自微信公众号“沉默王二”,可通过以下二维码关注。转载本文请联系沉默王二公众号。
