如果使用的jdk8以前版本的虚拟机,在拼接字符串时,会在jvm堆中生成StringBuilder对象,调用append方法拼接字符串,最后调用StringBuilder的toString方法在jvm堆中生成最终的字符串对象。
通过查看字节码就可以知道jdk8之前版本的"+"拼接字符串时通过StringBuilder实现的。通过查看字节码就可以知道,如下图所示:
而如果使用的是jdk9以后版本的虚拟机,则是调用虚拟机自带的InvokeDynamic拼接字符串,并且保存在堆中。字节码如下所示:
str4的对象在字符串常量池中,str5的对象在堆中,所以他们的不是同一个对象,所以返回的结果是false。
案例5:
String s5 = new String(“2”) + new String(“3”);和案例4一样,因为new String("2")创建字符串,也是在运行时才能确定对象内存地址,和案例4一样。
案例6:
final String str1 = "b"; String str2 = "a" + str1; String str3 = "ab"; System.out.println((str2 == str3));上面代码执行的结果为true。
str1是常量变量,在编译期就确定,直接放入到字符串常量池中,上面的代码效果等同于:
String str2 = "a" + "b"; String str3 = "ab"; System.out.println((str2 == str3));调用String类的intern()方法,会将堆中的字符串实例放入到字符串常量池中。
案例7:
String str2 = "ab"; String str3 = "cd"; String str4 = str2 + str3 + "1"; str4.intern(); String str5 = "abcd1"; System.out.println((str4==str5));上面代码执行的结果:true。调用了str4.intern()方法后,将str4放入到字符串常量池中,和str5是同一个实例。
StringBuffer部分源码:
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, Comparable<StringBuffer>, CharSequence {StringBuilder部分源码:
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, Comparable<StringBuilder>, CharSequence {可见StringBuffer和StringBuilder都继承了AbstractStringBuilder类。
AbstractStringBuilder类源码:
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ byte[] value;AbstractStringBuilder也有一个字节数组的成员变量value,这个变量用于存储字符串的值,这个变量不是用final修饰,所以是可以改变的,这个是和String的最大区别。
在调用append方法的时候,会动态增加字节数组变量value的大小。
StringBuffer和StringBuilder功能是一样的,都是为了提高java中字符串连接的效率,因为直接使用+进行字符串连接的话,jvm会创建多个String对象,因此造成一定的开销。AbstractStringBuilder中采用一个byte数组来保存需要append的字符串,byte数组有一个初始大小,当append的字符串长度超过当前char数组容量时,则对byte数组进行动态扩展,也即重新申请一段更大的内存空间,然后将当前bute数组拷贝到新的位置,因为重新分配内存并拷贝的开销比较大,所以每次重新申请内存空间都是采用申请大于当前需要的内存空间的方式,这里是2倍。
StringBuffer和StringBuilder最大的区别是StringBuffer是线程安全,而StringBuilder是非线程安全的,从它们两个类的源码就可以知道,StringBuffer类的方法前面都是synchronized修饰符。
String一旦赋值或实例化后就不可更改,如果赋予新值将会重新开辟内存地址进行存储。
而StringBuffer和StringBuilder类使用append和insert等方法改变字符串值时只是在原有对象存储的内存地址上进行连续操作,减少了资源的开销。
总结:
1、频繁使用“+”操作拼接字符时,换成StringBuffer和StringBuilder类的append方法实现。
2、多线程环境下进行大量的拼接字符串操作使用StringBuffer,StringBuffer是线程安全的;
3、单线程环境下进行大量的拼接字符串操作使用StringBuilder,StringBuilder是线程不安全的。