Java:String類再回顧

字符串廣泛應用 在 Java 編程中,在 Java 中字符串屬於對象,Java 提供了 String 類來創建和操作字符串。 String
類是不可改變的,所以一旦創建了 String 對象,那它的值就無法改變了。 如果需要對字符串做很多修改,那麼應該選擇使用
StringBuffer & StringBuilder 類

爲什麼String類是不可改變的?

String s = "AAAA";
System.out.println("s = " + s);

s = "BBBB";
System.out.println("s = " + s);

輸出結果:

AAAA
BBBB

從結果上看是改變了,但爲什麼說String對象是不可變的呢?

原因在於實例中的 s 只是一個 String 對象的引用,並不是對象本身,當執行 s = “BBBB”; 創建了一個新的對象 “BBBB”,而原來的 “AAAA” 還存在於內存中。

如圖:

或者根據 jdk 的源碼來分析。

字符串實際上就是一個 char 數組,並且內部就是封裝了一個 char 數組。

並且這裏 char 數組是被 final 修飾的:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

並且 String 中的所有的方法,都是對於 char 數組的改變,只要是對它的改變,方法內部都是返回一個新的 String 實例。


Java:String、StringBuffer 和 StringBuilder 的區別

String:字符串常量,字符串長度不可變。Java中String 是immutable(不可變)的。用於存放字符的數組被聲明爲final的,因此只能賦值一次,不可再更改。

StringBuffer:字符串變量(Synchronized,即線程安全)。如果要頻繁對字符串內容進行修改,出於效率考慮最好使用 StringBuffer,如果想轉成 String 類型,可以調用 StringBuffer 的 toString() 方法。Java.lang.StringBuffer 線程安全的可變字符序列。在任意時間點上它都包含某種特定的字符序列,但通過某些方法調用可以改變該序列的長度和內容。可將字符串緩衝區安全地用於多個線程。

StringBuilder:字符串變量(非線程安全)。在內部 StringBuilder 對象被當作是一個包含字符序列的變長數組。

基本原則:

  • 如果要操作少量的數據用 String ;
  • 單線程操作大量數據用StringBuilder ;
  • 多線程操作大量數據,用StringBuffer。

String類支持的方法:

  • 獲取字符串長度 用於獲取有關對象的信息的方法稱爲訪問器方法。

    • String 類的一個訪問器方法是 length() 方法
      • 1、length() 方法是針對字符串來說的,要求一個字符串的長度就要用到它的length()方法;
      • 2、length 屬性是針對 Java 中的數組來說的,要求數組的長度可以用其 length 屬性;
      • 3、Java 中的 size() 方法是針對泛型集合說的, 如果想看這個泛型有多少個元素, 就調用此方法來查看!
  • 連接字符串

    • concat() 方法

    • '+'操作符

      • String a = "a";
        String b = "b";
        String c = a + b;

        相當於:

        String c = new StringBuffer().append(a).append(b).toString();

        對於字符串的加運算,當編譯成 class 文件時,會自動編譯爲 StringBuffer 來進行字符串的連接操作。

        同時對於字符串常量池:

        當一個字符串是一個字面量時,它會被放到一個常量池中,等待複用。

        String a = "saff";
        String b = "saff";
        String c = new String("saff");
        System.out.println(a.equal(b));  // true
        System.out.println(a.equal(c));  // true

        這個就是字符串的常量池。

  • 創建格式化字符串

    • String 類的靜態方法 format() 能用來創建可複用的格式化字符串,而不僅僅是用於一次打印輸出。
  • 返回指定索引處的 char 值

    • charAt() 方法用於返回指定索引處的字符。索引範圍爲從 0 到 length() - 1。
  • 字符串與對象進行比較按字典順序比較兩個字符串

    • int compareTo(Object o)

    • compareTo(String anotherString)

      • 關於這個方法,不管參數是對象還是字符串,最終要比較的都是兩個字符串的不同,以下稱調用方法那邊的爲原字符串,方法參數裏的爲參數字符串。

        這個方法分爲兩種比較方式:

        1、不同的字符在較短字符串長度之內時

        2、不同的字符在較短字符串長度之外時

        1. 看下 compareTo 的源碼:

          /*
          *如果參數字符串等於此字符串,則返回值 0;
          *如果此字符串按字典順序小於字符串參數,則*返回一個小於 0 的值;
          *如果此字符串按字典順序大於字符串參數,則返回一個大於 0 的值。
          */
          public int compareTo(String anotherString) {
              int len1 = value.length;
              int len2 = anotherString.value.length;
              //取數組長度裏面最小的
              int lim = Math.min(len1, len2);
              // 獲得兩個數組,這兩個數組就是string的屬性
              char v1[] = value;
              char v2[] = anotherString.value;
              int k = 0;
              while (k < lim) {
              //獲取第K的字符,進行比較
                  char c1 = v1[k];
                  char c2 = v2[k];
                  if (c1 != c2) {
                  //Java使用的是Unicode編碼,因此返回這兩個字符的Unicode差值。
                      return c1 - c2;
                  }
                  k++;
              }
              //如果前lim個字符都相同,那麼就返回長度差。
              return len1 - len2;
          }
          • 1、取得string的value數組
          • 2、取得value數組裏面的元素
          • 3、按照unicode值進行比較
          • 4、返回比較的值
          String a = "a";
          String b = "b";
          System.out.println(a.compareTo(b));

          輸出值 -1

          String a = "b";
          String b = "a";
          System.out.println(a.compareTo(b));

          輸出值 1

          String a = "a";
          String b = "a";
          System.out.println(a.compareTo(b));

          輸出 0

          兩個字符串首字母不同,則該方法返回首字母的 asc 碼的差值。

          String a = "abc";
          String b = "bcdfg";
          System.out.println(a.compareTo(b));

          輸出 -1。

          參與比較的兩個字符串如果首字符相同,則比較下一個字符,直到有不同的爲止,返回該不同的字符的 ascii 碼差值。

          String a = "abc";
          String b = "abedfg";
          System.out.println(a.compareTo(b));

          輸出 -2。

          兩個字符串不一樣長,可以參與比較的字符又完全一樣,則返回兩個字符串的長度差值。

          String a = "abc";
          String b = "abcdefg";
          System.out.println(a.compareTo(b));

          輸出 -4。

          string a = "abcde";
          String b = "abcd";
          System.out.println(a.compareTo(b));

          輸出 1

          目前 compareTo 項目中的用途是比較版本號的高低。

          String a = "1.0.0";
          String b = "1.0.1";
          System.out.println(a.compareTo(b));

          輸出 -1

  • 按字典順序比較兩個字符串,不考慮大小寫

    • compareToIgnoreCase()

      • 關於這個方法,不管參數是對象還是字符串,最終要比較的都是兩個字符串的不同,以下稱調用方法那邊的爲原字符串,方法參數裏的爲參數字符串。

        這個方法分爲兩種比較方式:

        1、不同的字符在較短字符串長度之內時

        返回值=原字符串與參數字符串中第一個不同字符相差的ASCII碼值,爲原減參。

        例子如下:

        String str1="javDscrspt";
        String str2="jAvascript";
        str1.compareToIgnoreCase(str2);

        此時返回值爲3,是d的ASCII碼(100)減去了a的ASCII碼值(97)得到或者D與A相差得到的。

        注意:只比較第一個不同(這個方法裏不考慮字母大小寫)的字符,後面的s和i也不一樣但不會進行比較了,無關字母大小寫所以只比較相同格式下相差的ASCII碼值。

        2、不同的字符在較短字符串長度之外時

        返回=原字符串與參數字符串相差的字符個數,原字符串長度大時爲正,反之爲負。

        例子如下:

        String str1="javAScript";
        String str2="JaVa";
        str1.compareToIgnoreCase(str2);

        此時返回值爲6,是str1相比str2多出來的字符個數。

        注意:此時只比較位數,而無關ASCII碼值,並非是S(s)的ASCII碼值減去0的ASCII碼值,在參數字符串前面字符和原字符串一樣時,返回值就是兩者相差的字符個數,即使改變後面的字符也不會影響到返回的值,比如String str1=“jAva233666”,此時結果仍是6。

  • 當且僅當字符串與指定的StringBuffer有相同順序的字符時候返回真

    • boolean contentEquals(StringBuffer sb)
  • 返回指定數組中表示該字符序列的 String

    • static String copyValueOf(char[] data)
    • static String copyValueOf(char[] data, int offset, int count)
  • 測試此字符串是否以指定的後綴結束

    • boolean endsWith(String suffix)
  • 將字符串與指定的對象比較,不考慮大小寫

    • boolean equalsIgnoreCase(String anotherString)
  • 使用平臺的默認字符集將此 String 編碼爲 byte 序列,並將結果存儲到一個新的 byte 數組中

    • byte[] getBytes()

      • 可用如下方法查看字符串用 getBytes() 方法處理後返回的 byte[] 數組中的內容:

        class Main {
            public static void main(String[] args) {
            String str = "make a fortune";
            byte[] byt = str.getBytes();
                for (byte b : byt) {
                    System.out.println(b);
                }
            }
        }

        以上程序運行結果爲:

        109
        97
        107
        101
        32
        97
        32
        102
        111
        114
        116
        117
        110
        101

        可見 byte[] 數組中存放的是字符串響應位置對應的字母的哈希值,如字符串中的字母 a 對應 byte[] 數組中的 97 。

        另外:返回的 byte[] 數組的長度,與原字符串的長度相等

  • 使用指定的字符集將此 String 編碼爲 byte 序列,並將結果存儲到一個新的 byte 數組中

    • byte[] getBytes(String charsetName)
  • 將字符從此字符串複製到目標字符數組

    • void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
  • 返回此字符串的哈希碼

    • int hashCode()
  • 返回指定字符在此字符串中第一次出現處的索引

    • int indexOf()
  • 返回字符串對象的規範化表示形式

    • String intern()

      • 儘管在輸出中調用intern方法並沒有什麼效果,但是實際上後臺這個方法會做一系列的動作和操作。在調用”ab”.intern()方法的時候會返回”ab”,但是這個方法會首先檢查字符串池中是否有”ab”這個字符串,如果存在則返回這個字符串的引用,否則就將這個字符串添加到字符串池中,然會返回這個字符串的引用。

        可以看下面一個範例:

        String str1 = "a";
        String str2 = "b";
        String str3 = "ab";
        String str4 = str1 + str2;
        String str5 = new String("ab");
         
        System.out.println(str5.equals(str3));
        System.out.println(str5 == str3);
        System.out.println(str5.intern() == str3);
        System.out.println(str5.intern() == str4);

        得到的結果:

        true
        false
        true
        false

        爲什麼會得到這樣的一個結果呢?我們一步一步的分析。

        • 第一、str5.equals(str3)這個結果爲true,不用太多的解釋,因爲字符串的值的內容相同。
        • 第二、str5 == str3對比的是引用的地址是否相同,由於str5採用new String方式定義的,所以地址引用一定不相等。所以結果爲false。
        • 第三、當str5調用intern的時候,會檢查字符串池中是否含有該字符串。由於之前定義的str3已經進入字符串池中,所以會得到相同的引用。
        • 第四,當str4 = str1 + str2後,str4的值也爲”ab”,但是爲什麼這個結果會是false呢?先看下面代碼:
        String a = new String("ab");
        String b = new String("ab");
        String c = "ab";
        String d = "a" + "b";
        String e = "b";
        String f = "a" + e;
        
        System.out.println(b.intern() == a);
        System.out.println(b.intern() == c);
        System.out.println(b.intern() == d);
        System.out.println(b.intern() == f);
        System.out.println(b.intern() == a.intern());

        運行結果:

        false
        true
        true
        false
        true

        由運行結果可以看出來,b.intern() == a和b.intern() == c可知,採用new 創建的字符串對象不進入字符串池,並且通過b.intern() == d和b.intern() == f可知,字符串相加的時候,都是靜態字符串的結果會添加到字符串池,如果其中含有變量(如f中的e)則不會進入字符串池中。但是字符串一旦進入字符串池中,就會先查找池中有無此對象。如果有此對象,則讓對象引用指向此對象。如果無此對象,則先創建此對象,再讓對象引用指向此對象。

        當研究到這個地方的時候,突然想起來經常遇到的一個比較經典的Java問題,就是對比equal和的區別,當時記得老師只是說“”判斷的是“地址”,但是並沒說清楚什麼時候會有地址相等的情況。現在看來,在定義變量的時候賦值,如果賦值的是靜態的字符串,就會執行進入字符串池的操作,如果池中含有該字符串,則返回引用。

        執行下面的代碼:

        String a = "abc";
        String b = "abc";
        String c = "a" + "b" + "c";
        String d = "a" + "bc";
        String e = "ab" + "c";
                
        System.out.println(a == b);
        System.out.println(a == c);
        System.out.println(a == d);
        System.out.println(a == e);
        System.out.println(c == d);
        System.out.println(c == e);

        運行的結果:

        true
        true
        true
        true
        true
        true
  • 返回指定字符在此字符串中最後一次出現處的索引

    • lastIndexOf()
  • 檢測字符串是否匹配給定的正則表達式

    • matches()
  • 測試兩個字符串區域是否相等

    • boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
  • 替換爲新字符串

    • replace()
  • 使用給定的 replacement 替換此字符串所有匹配給定的正則表達式的子字符串

    • replaceAll()
  • 使用給定的參數 replacement 替換字符串第一個匹配給定的正則表達式的子字符串

    • replaceFirst()
  • 根據給定正則表達式的匹配拆分此字符串

    • split()

      • . 必須得加轉義字符**\**,以下是拆分 IP 地址的實例:

        public class Demo {
            public static void main(String args[]) {
                String str = "192.168.1.1";
                // . 必須得加 轉義字符\\
                for(String s : str.split("\\.")){
                    System.out.println(s);
                }
            }
        }
  • 檢測字符串是否以指定的前綴開始

    • startsWith()
  • 返回一個新的字符序列,它是此序列的一個子序列

    • subSequence() (返回的是String)
    • substring() (返回的是實現了CharSequence接口的類,可以直接下轉爲String對象)
  • 將字符串轉換爲字符數組

    • toCharArray()
  • 將字符串轉換爲小寫

    • toLowerCase()
  • 返回此對象本身

    • toString()
  • 將字符串小寫字符轉換爲大寫

    • toUpperCase()
  • 用於刪除字符串的頭尾空白符

    • trim()
  • 返回給定data type類型x參數的字符串表示形式

    • valueOf()

System.out.println(s);
}
}
}
```

  • 檢測字符串是否以指定的前綴開始

    • startsWith()
  • 返回一個新的字符序列,它是此序列的一個子序列

    • subSequence() (返回的是String)
    • substring() (返回的是實現了CharSequence接口的類,可以直接下轉爲String對象)
  • 將字符串轉換爲字符數組

    • toCharArray()
  • 將字符串轉換爲小寫

    • toLowerCase()
  • 返回此對象本身

    • toString()
  • 將字符串小寫字符轉換爲大寫

    • toUpperCase()
  • 用於刪除字符串的頭尾空白符

    • trim()
  • 返回給定data type類型x參數的字符串表示形式

    • valueOf()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章