1.代码
jdk版本是1.8.0_111
代码如下:
对Test4.class的代码执行javap命令,如下:
执行后截图如下:
2.执行字节码指令的过程
第0行:
为str分配内存空间,并把该引用放到操作数栈中。
第3行:
复制操作数栈栈顶的元素,并放到操作数栈中。
第4行:
把指向常量池中引用(暂时为该引用命名为sym)放到操作数栈栈顶。
第6行:
调用String的String(String str)实例构造器,执行new String(sym),需要使用操作数栈的最上面的两个元素,并把new String()的引用放到操作数栈中。
第9行:
把栈顶的指向String实例的引用放到局部变量表的索引为1的位置中。
3.String的源码
可以看出来,value是一个char型数组,而且是final修饰的。
4.综合字节码指令和String源码
在第2小节中,可以看到new String("abc")其实是执行的是第6行的new String(sym),又因为sym为指向常量池中"abc"的引用,具体来说是因为sym是String类型(可以从常量池中的#18看出来),所以sym中有value数组,也就是value数组中存储的是"abc",所以在执行this.value = original.value时,是把sym中的value引用赋值给this.value。
new String("abc")相当于new String(sym)
value是数组,所以value也是引用,指向数组对象(这里的value是private的,但是original是String类型的且在String中调用,所以original.value可以直接调用,不会报编译时不可见错误)。
综上,在执行str = new String("abc")时,会在编译期时,在常量池中放入一个"abc",在执行期时,把str中的value指向常量池中的"abc"。所以图如下(图片来源:https://www.jianshu.com/p/2f209af80f84):
另外的StringBuilder和StringBuffer和String的区别的原因可通过同样的方式进行字节码查询,举例如下:
这就是当进行大量字符串连接时,不推荐使用string的原因,因为每次进行一条包含诸如s12 = s1 + s2;这样的操作,都是要在内存中创建一个StringBuilder或StringBuffer的操作。
而且String中的+号的实现通过字节码可以看出,+号在实现的过程就是执行append方法的过程。
参考:
浅谈Java String内幕:https://www.jianshu.com/p/2f209af80f84
Java字节码的介绍:https://blog.csdn.net/hj7jay/article/details/80014151