1. 程式人生 > 實用技巧 >Java基礎-String字串

Java基礎-String字串

  • String是一種引用資料型別

  • String的值儲存在字串的常量池中,在JDK1.8中,字串常量池儲存於元空間中,JDK1.7中,字串常量池儲存於堆記憶體

  • 宣告一個String型別的變數有兩種方法

    • 直接賦值,使用這種方法宣告的字串棧記憶體中的指標直接指向字串常量池中的值

      String str1 = "hello";
      
    • 建立String類的物件,使用這種方法宣告的字串棧記憶體中的指標指向堆記憶體中開闢的記憶體空間,之後再由堆記憶體指向字串常量池中的值

      String str2 = new String("hello");
      
  • 字串的比較

    • 直接賦值宣告的兩個字串可以使用==

      來比較

      • 例如

        String str1 = "hello";
        String str2 = "hello";
        String str3 = "hi";
        System.out.println(str1 == str2);//結果為true
        System.out.println(str1 == str3);//結果為false
        
  • 在使用==比較使用建立String物件的方式宣告的字串時,即使它們的值相等,也可能出現結果為false的現象

    • 原因:String是引用資料型別,==比較的是左右兩邊的記憶體地址

    • 例如:

        String str1 = "hello";
        String str2 = new String("hello");
        System.out.println(str1 == str2);//結果為false
      

      輸出的結果為false,兩個字串雖然值相同,但是宣告的方式不同,在記憶體中儲存的方式有所不同。比較時,實際上str1獲取的地址是字串常量池中值的地址,而str2獲取的是堆記憶體中,String物件的地址,兩個地址並不相同,因此結果為false。

        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println(str1 == str2);//結果為false
      

      上述程式碼輸出結果也為false,因為str1和str2雖然使用的宣告方法相同,值也相同,但是兩個獨立String物件,在堆記憶體中開闢的記憶體地址不同。

  • 使用.equals方法可以直接比較兩個字串的值

    • 例如:

        String str1 = "hello";
        String str2 = new String("hello");
        System.out.println(str1.equals(str2));//結果為true
      
    • 原因:String類重寫了equals方法,先比較兩個字串地址是否相同,如果相同返回true,如果不同判斷傳入的引數是否為String型別,如果不是返回flase,如果是那麼進行值的一一比較。

    • String類中的equals方法原始碼

        public boolean equals(Object anObject) {
            //如果引用的是同一個物件,返回真
            if (this == anObject) {
                return true;
            }
            //如果不是String型別的資料,返回假
            if (anObject instanceof String) {
                String anotherString = (String) anObject;
                int n = value.length;
                //如果char陣列長度不相等,返回假
                if (n == anotherString.value.length) {
                    char v1[] = value;
                    char v2[] = anotherString.value;
                    int i = 0;
                    //從後往前單個字元判斷,如果有不相等,返回假
                    while (n-- != 0) {
                        if (v1[i] != v2[i])
                                return false;
                        i++;
                    }
                    //每個字元都相等,返回真
                    return true;
                }
            }
            return false;
        }
      
  • 字串的拼接

    • 字串直接可以使用+進行拼接

    • 例如:

      String str1 = "123";
      String str2 = "456";
      String str3 = str1 + str2;
      String str4 = "123" + "456";
      System.out.println(str3);//輸出結果為123456
      System.out.println(str4);//輸出結果為123456
      
    • 根據拼接方法的不同,使用==比較拼接過的字串和一個值與其相同的字串可能會有不同的結果

    • 使用變數名進行拼接

      • 例如:

        String str1 = "123";
        String str2 = "456";
        String str3 = str1 + str2;
        String str4 = "123456";
        System.out.println(str3 == str4);//結果為false
        

        使用這種拼接方法時,根據對.class檔案的反編譯可知,這樣拼接字串時實際上呼叫了StringBuilder的append方法,返回了一個String物件,因此結果為false。

        • 對樣例程式碼的反編譯結果

          public class Test {
            public Test();
              Code:
                 0: aload_0
                 1: invokespecial #1                  // Method java/lang/Object."<init>":()V
                 4: return
          
            public static void main(java.lang.String[]);
              Code:
                 0: ldc           #2                  // String 123
                 2: astore_1
                 3: ldc           #3                  // String 456
                 5: astore_2
                 6: new           #4                  // class java/lang/StringBuilder
                 9: dup
                10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
                13: aload_1
                14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
                17: aload_2
                18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
                21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
                24: astore_3
                25: ldc           #8                  // String 123456
                27: astore        4
                29: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
                32: aload_3
                33: aload         4
                35: if_acmpne     42
                38: iconst_1
                39: goto          43
                42: iconst_0
                43: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
                46: return
          }
          
        • StringBuilder類的append方法原始碼

          public AbstractStringBuilder append(String str) {
              if (str == null)
                  return appendNull();
              int len = str.length();
              ensureCapacityInternal(count + len);
              str.getChars(0, len, value, count);
              count += len;
              return this;
          }
          
    • 使用值進行拼接

      • 例如

        String str1 = "123" + "456";
        String str2 = "123456";
        System.out.println(str1 == str2);//結果為true
        

        使用這中方法進行拼接時,通過對.class檔案的反編譯可知,在編譯時,編譯器對這種情況進行了優化,str1相當於直接賦值聲明瞭一個值為123456的String型別變數

        • 對樣例程式碼的反編譯結果

          public class Test {
            public Test();
              Code:
                 0: aload_0
                 1: invokespecial #1                  // Method java/lang/Object."<init>":()V
                 4: return
          
            public static void main(java.lang.String[]);
              Code:
                 0: ldc           #2                  // String 123456
                 2: astore_1
                 3: ldc           #2                  // String 123456
                 5: astore_2
                 6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
                 9: aload_1
                10: aload_2
                11: if_acmpne     18
                14: iconst_1
                15: goto          19
                18: iconst_0
                19: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
                22: return
          }