第一行代码 Strings1=newString("hello ") newString("world");的执行过程是这样子的:
1.依次在堆内存中创建"hello "和"world"两个字符串对象
2.然后把它们拼接起来 (底层使用StringBuilder实现, 后面会带大家读反编译代码)
3.在拼接完成后会产生新的"hello world"对象, 这时变量s1指向新对象"hello world"
执行完第一行代码后, 内存是这样子的:
第二行代码 s1.intern();
String类的源码中有对 intern()方法的详细介绍, 翻译过来的意思是: 当调用 intern()方法时, 首先会去常量池中查找是否有该字符串对应的引用, 如果有就直接返回该字符串; 如果没有, 就会在常量池中注册该字符串的引用, 然后返回该字符串。
由于第一行代码采用的是new的方式创建字符串, 所以在字符串常量池中没有保存"hello world"对应的引用, 虚拟机会在常量池中进行注册, 注册完后的内存示意图如下:
第三行代码 Strings2="hello world";
这种直接通过双引号""声明字符串背后的运行机制我们在第一个案例提到过, 这里正好复习一下。
首先虚拟机会去检查字符串常量池, 发现有指向"hello world"的引用. 然后把该引用所指向的字符串直接返回给所属变量。
执行完第三行代码后, 内存示意图如下:
如图所示, s1和s2指向的是相同的对象, 所以当判断s1 == s2时返回true。
最后我们对字符串常量池进行总结:
当用new关键字创建字符串对象时, 不会查询字符串常量池; 当用双引号直接声明字符串对象时, 虚拟机将会查询字符串常量池. 说白了就是: 字符串常量池提供了字符串的复用功能, 除非我们要显式创建新的字符串对象, 否则对同一个字符串虚拟机只会维护一份拷贝。
配合反编译代码验证字符串初始化操作.相信看到这里, 再见到有关的面试题, 你已经无所畏惧了, 因为你已经懂得了背后原理。
在结束之前我们不妨再做一道压轴题