关于String字符串相加的问题
网上很多人说是创建一个StringBuilder对象,再进行append追加,我不清楚是不是这样,Debug了一下代码,发现好像不是这样,分以下几种情况:
- 两个new的字符串相加(底层调用的数组拷贝,然后创建String对象)
System.out.println("============");
// 两个newString相加得到一个新String对象,即s1指向堆中的一个新的String对象
String s1 = new String("hello") + new String("123");
// s2直接指向字符串常量池"hello123"的内存地址
String s2 = "hello123";
// s3是"hello"与"123"字符串拼接得到"hello123",也是直接指向字符串常量池"hello123"的内存地址
String s3 = "hello" + "123";
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true
- 两个String字符串相加(底层同样是调用的数组拷贝的方法,再通过byte数组创建对象)
// 两个String字符串直接相加其实和第一种情况是一样的
// 第一种是先创建好了两个String,这一种是s1,s2直接指向方法区常量池中的字符串内存地址
String s1="a";
String s2="b";
// 这里依旧会再new 一个String对象,底层同样调用的数组拷贝
String s3=s1+s2;
String s4="ab";
System.out.println(s3 == s4); // false
- 对第一种和第二种的解释:Debug打断点
进入MethodHandle方法
进入invokeStatic方法
进入到StringConcatHelper类,这个类是来自String字符串拼接的工厂,在这里执行simpleConcat方法,参数为两个相加的字符串
在这个方法里首先会把两个参数转化为String类型
接着判断参数个数,根据个数创建一个byte[]数组
进入这个方法之后会调用数组拷贝的方法
进入newString方法,并将数组作为参数传入
调用String类的构造方法
这个时候就创建好了一个String对象,s3指向这个String对象,并引用其内存地址。
并且我反编译了字节码文件,发现底层使用的是StringConcatFactory工厂的方法:
SourceFile: "TestString.java"
BootstrapMethods:
0: #52 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#58 \u0001\u0001
InnerClasses:
public static final #65= #61 of #63; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
- 直接两个字符串相加
System.out.println("==========");
// 两个字符串直接相加,底层JVM会直接将两个字符串拼接,
// 如果拼接后的字符串存在于常量池则直接指向其地址,
// 否则在常量池创建一个字符串再指向其地址
String z1 = "he" + "lm";
String z2 = "helm";
System.out.println(z1 == z2); // true