首先需要知道的是什么是字符串常量和字符串变量:
字符串常量:如”abc”
字符串变量:如String str1=”abc” 或者 String str2=new String(“abc”)
两种实例化方式,其中
String str1=”abc” 采用的是直接赋值的方式
String str2=new String(“abc”) 采用的是构造方法的方式
由于在JVM底层实际上会自动维护一个对象池(字符串对象池),如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中。如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用;如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用。但是如果采用的是构造方法的方式进行String类的对象实例化操作,那么此时会开辟两块堆空间,其中存放字符串常量的那块空间将成为垃圾空间,并且此时产生的实例化对象不会保存到对象池中。
String类中的==与equals:
==比较的是字符串地址值的内容;equals比较的是字符串的内容【覆写了Object类的equals方法】
下面看一段代码:
public class StringTest {
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
String str3 = "ab";
String str4 = "c";
String str5 = str3 + str4;
String str6 = str3 + str4;
String str7 = str3 + "c"; //这里的str3是变量
String str8 = "ab" + "c"; //这里的"ab"、"c"是字符串常量 "ab"+"c"="abc"
System.out.println(str1 == str2); //false 只要出现了new,就一定开辟了新空间
System.out.println(str1 == str5); //false
System.out.println(str1 == str6); //false
System.out.println(str5 == str6); //false
System.out.println(str5 == (str3 + str4)); //false
System.out.println(str1 == str7); //false
System.out.println(str1 == str8); //true
}
}
通过反编译我们来了解一下部分字节码:
//public static void main(java.lang.String[]);
// descriptor: ([Ljava/lang/String;)V
// flags: ACC_PUBLIC, ACC_STATIC
// Code:
// stack=4, locals=9, args_size=1
// 0: ldc #2 // String abc str1
// 2: astore_1
// 3: new #3 // class java/lang/String new:产生一个String对象
// 6: dup
// 7: ldc #2 // String abc
// 9: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V str2
// 12: astore_2
// 13: ldc #5 // String ab str3
// 15: astore_3
// 16: ldc #6 // String c str4
// 18: astore 4
// 20: new #7 // class java/lang/StringBuilder new:产生一个StringBuilder对象
// 23: dup
// 24: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
// 27: aload_3
// 28: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 往StringBuilder对象中添加元素"ab"
// 31: aload 4
// 33: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 往StringBuilder对象中添加元素"c"
// 36: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 调用StringBuilder对象的toString方法返回一个String对象 str5
// 39: astore 5
// 41: new #7 // class java/lang/StringBuilder new:产生一个StringBuilder对象
// 44: dup
// 45: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
// 48: aload_3
// 49: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 往StringBuilder对象中添加元素"ab"
// 52: aload 4
// 54: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 往StringBuilder对象中添加元素"c"
// 57: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 调用StringBuilder对象的toString方法返回一个String对象 str6
// 60: astore 6
// 62: new #7 // class java/lang/StringBuilder new:产生一个StringBuilder对象
// 65: dup
// 66: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
// 69: aload_3
// 70: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 往StringBuilder对象中添加元素"ab"
// 73: ldc #6 // String c
// 75: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 往StringBuilder对象中添加元素"c"
// 78: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 调用StringBuilder对象的toString方法返回一个String对象 str7
// 81: astore 7
// 83: ldc #2 // String abc str8
// 85: astore 8
// 87: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
// 90: aload_1
// 91: aload_2
// 92: if_acmpne 99
// 95: iconst_1
// 96: goto 100
// 99: iconst_0
// 100: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
// 103: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
// 106: aload_1
// 107: aload 5
// 109: if_acmpne 116
// 112: iconst_1
// 113: goto 117
// 116: iconst_0
// 117: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
// 120: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
// 123: aload_1
// 124: aload 6
// 126: if_acmpne 133
// 129: iconst_1
// 130: goto 134
// 133: iconst_0
// 134: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
// 137: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
// 140: aload 5
// 142: aload 6
// 144: if_acmpne 151
// 147: iconst_1
// 148: goto 152
// 151: iconst_0
// 152: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
// 155: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
// 158: aload 5
// 160: new #7 // class java/lang/StringBuilder
// 163: dup
// 164: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
// 167: aload_3
// 168: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
// 171: aload 4
// 173: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
// 176: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
// 179: if_acmpne 186
// 182: iconst_1
// 183: goto 187
// 186: iconst_0
// 187: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
// 190: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
// 193: aload_1
// 194: aload 7
// 196: if_acmpne 203
// 199: iconst_1
// 200: goto 204
// 203: iconst_0
// 204: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
// 207: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
// 210: aload_1
// 211: aload 8
// 213: if_acmpne 220
// 216: iconst_1
// 217: goto 221
// 220: iconst_0
// 221: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
// 224: return
总结:
1. 在String类中,只要有字符串变量的”+”,编译器都会将其优化为StringBuilder,而后调用append方法进行字符串的拼接,然后调用toString方法转为String(toString方法内部使用new String 返回String类的实例化对象)
所以在进行字符串变量相加后所得的字符串一定是一个新的字符串(因为开辟了新空间)
注:一个字符串常量和一个字符串变量相加时也是要开辟新空间
2. 字符串常量相加后进行比较时,看的是常量池中是否有此相加后的字符串,如果有则==为true