String类中 + 的奥秘

首先需要知道的是什么是字符串常量和字符串变量:

字符串常量:如”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类的实例化对象)

String类中 + 的奥秘

所以在进行字符串变量相加后所得的字符串一定是一个新的字符串(因为开辟了新空间)  

注:一个字符串常量和一个字符串变量相加时也是要开辟新空间

2. 字符串常量相加后进行比较时,看的是常量池中是否有此相加的字符串,如果有则==为true

上一篇:springboot启动报错:java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava


下一篇:元空间、堆、线程在内存中的联系