一篇講清楚String、StringBuffer和StringBuild

歸~海發表於2022-05-01

 

一篇講清楚String、StringBuffer和StringBuild

一、String篇

1、String基本介紹?

(jdk文件原文)String類代表字串。 Java程式中的所有字串文字(例如"abc" )都被實現為此類的例項。

說人話就是:String是用來儲存字串的,比如:“我好帥啊”、“123456”、"hello"這些都是字串,而區分是否為字串的標誌就是這對雙引號:""。

2、String類特性:

  • String是一個final類,代表不可變的字元序列。

  • 字串是常量,用雙引號引起來表示。它們的值在建立之後不能更改。

  • String物件的字元內容是儲存在一個字元陣列value[]中的。

字串不變; 它們的值在建立後不能被更改。 字串緩衝區支援可變字串。 因為String物件是不可變的,它們可以被共享。 例如:

  String str = "abc";
​

相當於:

  char data[] = {'a', 'b', 'c'};
  String str = new String(data);
​

以下是一些如何使用字串的示例:

  System.out.println("abc");
  String cde = "cde";
  System.out.println("abc" + cde);
  String c = "abc".substring(2,3);
  String d = cde.substring(1, 2);

3、為什麼String是不可變的?

我們看看原始碼,發現value這個字元陣列被final修飾了,怪不得String是不可變的。

一篇講清楚String、StringBuffer和StringBuild

4、String的繼承圖以及父類介紹

 一篇講清楚String、StringBuffer和StringBuild

1)Serializable:

  • public interface Serializable

    類的序列化由實現java.io.Serializable介面的類啟用。不實現此介面的類將不會使任何狀態序列化或反序列化。可序列化類的所有子型別都是可序列化的。序列化介面沒有方法或欄位,僅用於標識可序列化的語義。

    為了允許序列化不可序列化的子型別,子型別可能承擔儲存和恢復超型別的公共,受保護和(如果可訪問)包欄位的狀態的責任。 子型別可以承擔此責任,只有當它擴充套件的類具有可訪問的無引數建構函式來初始化類的狀態。 如果不是這樣,宣告一個類Serializable是一個錯誤。 錯誤將在執行時檢測到。

    在反序列化期間,非可序列化類的欄位將使用該類的public或protected no-arg建構函式進行初始化。 對於可序列化的子類,必須可以訪問no-arg建構函式。 可序列化子類的欄位將從流中恢復。

    當遍歷圖形時,可能會遇到不支援Serializable介面的物件。 在這種情況下,將丟擲NotSerializableException,並將標識不可序列化物件的類

2)Comparable<T>:

  • public interface Comparable<T>

    該介面對實現它的每個類的物件強加一個整體排序。這個排序被稱為類的自然排序 ,類的compareTo方法被稱為其自然比較方法

    Collections.sort (和Arrays.sort )可以自動對實現此介面的物件進行列表(和陣列)排序。 實現該介面的物件,可以使用如在鍵sorted map或作為在元件sorted set ,而不需要指定一個comparator

    一類C的自然順序被說成是與equals一致當且僅當e1.compareTo(e2) == 0對每一個e1Ce2相同的布林值e1.equals(e2)。 請注意, null不是任何類的例項, e.compareTo(null)應該丟擲一個NullPointerException即使e.equals(null)返回false

    強烈建議(儘管不需要)自然排序與等於一致。 這是因為,當沒有顯式比較器的排序集(和排序對映)與其自然排序與equals不一致的元素(或鍵)一起使用時會“奇怪地”。 特別地,這種排序集合(或排序對映)違反了根據equals方法定義的集合(或對映)的一般合同。

3)CharSequence:

  • public interface CharSequence

    CharSequencechar值的可讀序列。該介面提供統一的,只讀訪問許多不同型別的char序列。char值代表基本多語言平面(BMP)或代理中的一個字元。詳見Unicode Character Representation

    此介面不會完善equalshashCode方法的一般合同。 因此,比較兩個物件實現CharSequence其結果是,在一般情況下,不確定的。 每個物件可以由不同的類實現,並且不能保證每個類都能夠測試其例項以與另一個類相同。 因此,使用任意的CharSequence例項作為集合中的元素或對映中的鍵是不合適的

 

5、建立 String 物件的兩種方式

方式一、直接賦值:String s = "歸海";

這種方式它首先會先從常量池檢視是否有"歸海" 這個資料空間,如果有就直接指向,如果沒有就建立一個”歸海“這個資料空間然後指向它。注意s最終指向的是常量池的空間地址。

方式二、呼叫構造器 String s1= new String("歸海");

這種方式則是先在堆中建立空間,裡面維護了value屬性,指向常量池的"歸海"空間。如果常量池中沒有''歸海'',則重新建立,如果有就直接通過value指向。注意這裡最終指向的是堆中的空間地址。

一篇講清楚String、StringBuffer和StringBuild

 

經過剛才簡單的介紹你應該對String有一點印象了,ok話不多說來幾道練習題:

例題一:

String a = "abc';

String b = "abc'';

System.out.println(a == b) ; //true/fales

System.out.println(a.equals(b));//true/fales

例題二:

String a = new String("abc");

String b = new String("abc");

System.out.println(a == b) ; //true/fales

System.out.println(a.equals(b));//true/fales

例題三:

String a = "歸海';

String b = new String("歸海");

System.out.println(a == b) ; //true/fales

System.out.println(a.equals(b));//true/fales

例題四:

Person p1 = new Person();

p1.name = "歸海";

Person p2 = new Person() ;

p2.name = "歸海";

System.out.println(p1.name.equals(p2.name));//true/fales

System.out.println(p1.name == p2.name) ; //true/fales

System.out.println(p1.name == "歸海") ; //true/fales

來說一下答案吧。

(1)T, T;

(2)F, T;

(3)F, T;

(4)T, T, T;

 

6、String 類常用方法

下面是 String 類支援的方法,更多詳細內容,參看 Java String API 文件:

序號方法描述
1 char charAt(int index) 返回指定索引處的 char 值。
2 int compareTo(Object o) 把這個字串和另一個物件比較。
3 int compareTo(String anotherString) 按字典順序比較兩個字串。
4 int compareToIgnoreCase(String str) 按字典順序比較兩個字串,不考慮大小寫。
5 String concat(String str) 將指定字串連線到此字串的結尾。
6 boolean contentEquals(StringBuffer sb) 當且僅當字串與指定的StringBuffer有相同順序的字元時候返回真。
7 [static String copyValueOf(char] data) 返回指定陣列中表示該字元序列的 String。
8 [static String copyValueOf(char] data, int offset, int count) 返回指定陣列中表示該字元序列的 String。
9 boolean endsWith(String suffix) 測試此字串是否以指定的字尾結束。
10 boolean equals(Object anObject) 將此字串與指定的物件比較。
11 boolean equalsIgnoreCase(String anotherString) 將此 String 與另一個 String 比較,不考慮大小寫。
12 [byte] getBytes() 使用平臺的預設字符集將此 String 編碼為 byte 序列,並將結果儲存到一個新的 byte 陣列中。
13 [byte] getBytes(String charsetName) 使用指定的字符集將此 String 編碼為 byte 序列,並將結果儲存到一個新的 byte 陣列中。
14 [void getChars(int srcBegin, int srcEnd, char] dst, int dstBegin) 將字元從此字串複製到目標字元陣列。
15 int hashCode() 返回此字串的雜湊碼。
16 int indexOf(int ch) 返回指定字元在此字串中第一次出現處的索引。
17 int indexOf(int ch, int fromIndex) 返回在此字串中第一次出現指定字元處的索引,從指定的索引開始搜尋。
18 int indexOf(String str) 返回指定子字串在此字串中第一次出現處的索引。
19 int indexOf(String str, int fromIndex) 返回指定子字串在此字串中第一次出現處的索引,從指定的索引開始。
20 String intern() 返回字串物件的規範化表示形式。
21 int lastIndexOf(int ch) 返回指定字元在此字串中最後一次出現處的索引。
22 int lastIndexOf(int ch, int fromIndex) 返回指定字元在此字串中最後一次出現處的索引,從指定的索引處開始進行反向搜尋。
23 int lastIndexOf(String str) 返回指定子字串在此字串中最右邊出現處的索引。
24 int lastIndexOf(String str, int fromIndex) 返回指定子字串在此字串中最後一次出現處的索引,從指定的索引開始反向搜尋。
25 int length() 返回此字串的長度。
26 boolean matches(String regex) 告知此字串是否匹配給定的正規表示式。
27 boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 測試兩個字串區域是否相等。
28 boolean regionMatches(int toffset, String other, int ooffset, int len) 測試兩個字串區域是否相等。
29 String replace(char oldChar, char newChar) 返回一個新的字串,它是通過用 newChar 替換此字串中出現的所有 oldChar 得到的。
30 String replaceAll(String regex, String replacement) 使用給定的 replacement 替換此字串所有匹配給定的正規表示式的子字串。
31 String replaceFirst(String regex, String replacement) 使用給定的 replacement 替換此字串匹配給定的正規表示式的第一個子字串。
32 [String] split(String regex) 根據給定正規表示式的匹配拆分此字串。
33 [String] split(String regex, int limit) 根據匹配給定的正規表示式來拆分此字串。
34 boolean startsWith(String prefix) 測試此字串是否以指定的字首開始。
35 boolean startsWith(String prefix, int toffset) 測試此字串從指定索引開始的子字串是否以指定字首開始。
36 CharSequence subSequence(int beginIndex, int endIndex) 返回一個新的字元序列,它是此序列的一個子序列。
37 String substring(int beginIndex) 返回一個新的字串,它是此字串的一個子字串。
38 String substring(int beginIndex, int endIndex) 返回一個新字串,它是此字串的一個子字串。
39 [char] toCharArray() 將此字串轉換為一個新的字元陣列。
40 String toLowerCase() 使用預設語言環境的規則將此 String 中的所有字元都轉換為小寫。
41 String toLowerCase(Locale locale) 使用給定 Locale 的規則將此 String 中的所有字元都轉換為小寫。
42 String toString() 返回此物件本身(它已經是一個字串!)。
43 String toUpperCase() 使用預設語言環境的規則將此 String 中的所有字元都轉換為大寫。
44 String toUpperCase(Locale locale) 使用給定 Locale 的規則將此 String 中的所有字元都轉換為大寫。
45 String trim() 返回字串的副本,忽略前導空白和尾部空白。
46 static String valueOf(primitive data type x) 返回給定data type型別x引數的字串表示形式。

不過發現沒String類的效率有點低啊!這是String類因為每次更新內容都要重新開闢空間,為此java設計者還提供了StringBuilder和SreingBuffer類來增強String功能和效率。

 

二、StringBuffer篇

1、StringBuffer基本介紹?

1)它也是一個元老級別的類了從jdk1.0的時候就有了

2)StringBuffer是一個可變的字元序列,可以對字元內容進行更改。

3)StringBuffer的很多方法也String相同,但是StringBuffer是可變長度的。

4)StringBuffer是一個容器。

2、StringBuffer的特性:

1)執行緒安全,可變的字元序列。 字串緩衝區就像一個String ,但可以修改。

2)字元緩衝可以安全的被多個執行緒使用。前提是這些方法必須進行同步。

3)每個字串緩衝區都有一個容量。 只要字串緩衝區中包含的字元序列的長度不超過容量,就不必分配新的內部緩衝區陣列。 如果內部緩衝區溢位,則會自動變大

一篇講清楚String、StringBuffer和StringBuild

 

private transient char[] toStringCache; 這是StringBuffer可以更改的原因。

3、StringBuffer注意事項:

StringBuffer類不同於String,其物件必須使用構造器生成。有三個構造器:

  • StringBuffer():初始容量為16的字串緩衝區

  • StringBuffer(int size):構造指定容量的字串緩衝區

  • StringBuffer(String str):將內容初始化為指定字串內容

4、StringBuffer的繼承圖以及父類介紹:

一篇講清楚String、StringBuffer和StringBuild

 

1)Appendable:

  • public interface Appendable

    可附加char序列和值的物件。Appendable介面必須由其例項旨在從Formatter接收格式化輸出的任何類實現

    要附加的字元應為Unicode Character Representation中描述的有效Unicode字元。 請注意,補充字元可以由多個16位char值組成。

    對於多執行緒訪問,附加功能不一定是安全的。 執行緒安全是擴充套件和實現這個介面的類的責任。

    由於此介面可能由具有不同樣式的錯誤處理的現有類實現,因此不能保證將錯誤傳播到呼叫者。

2)AbstractStringBuilder:首先這是一個類

位置:java.lang包中
宣告: abstract class AbstractStringBuilderimplements Appendable, CharSequence
AbstractStringBuilder 類有abstract 修飾,可知它不能被例項化。AbstractStringBuilder 類有兩個子類:StringBuilder和StringBuffer。

 

5、StringBuffer類常用方法:

序號方法描述
1 public StringBuffer append(String s) 將指定的字串追加到此字元序列。
2 public StringBuffer reverse() 將此字元序列用其反轉形式取代。
3 public delete(int start, int end) 移除此序列的子字串中的字元。
4 public insert(int offset, int i) 將 int 引數的字串表示形式插入此序列中。
5 replace(int start, int end, String str) 使用給定 String 中的字元替換此序列的子字串中的字元

 

6、String類和StringBuffer的區別:

1)String用於字串操作,屬於不可變類,而StringBuffer也是用於字串操作,不同之處是StringBuffer屬於可變類。

2) String是不可變類,也就是說,String物件一旦被建立,其值將不能被改變,而StringBuffer是可變類,當物件被建立後,仍然可以對其值進行修改。

3)String類每次更新實際上是更改地址,因此它的效率低。

4)StringBuffer類每次更新是更新內容,不用更新地址,因此它的效率高。

 

三、StringBuilder篇

1、StringBuilder基本介紹:

1)一個可變的的字元序列。提供了和SteingBuffer相容的API。

2)StringBuilder是執行緒不安全的,此類設計用作簡易替換為StringBuffer在正在使用由單個執行緒字串緩衝區的地方。

3)StringBuilder的主要StringBuilderappendinsert方法,它們是過載的,以便接受任何型別的資料。 每個都有效地將給定的資料轉換為字串,然後將該字串的字元附加或插入字串構建器。

4)它的速度比StringBuffer快畢竟執行緒不安全換來的。

2、StringBuilder的繼承圖以及原始碼:

一篇講清楚String、StringBuffer和StringBuild

我們會發現和StringBuffer一模一樣,所以它們的API相容也是正常。

一篇講清楚String、StringBuffer和StringBuild

原始碼也沒什麼說的,因為我也不會。

3、String、StringBuffer、StringBuilder的區別:

對比String、StringBuffer、StringBuilder

  • String(JDK1.0):不可變字元序列 ,效率低但是複用率高。

  • StringBuffer(JDK1.0):可變字元序列、效率較高、執行緒安全。

  • StringBuilder(JDK 5.0):可變字元序列、效率最高、執行緒不安全

注意:作為引數傳遞的話,方法內部String不會改變其值,StringBuffer和StringBuilder 會改變其值。

 

4、String、StringBuffer、StringBuilder的效率測試

程式碼例子

package link;

/**
 * @author 歸海
 * @date 2022/5/1
 */
public class Test {
        public static void main(String[] args) {

            long startTime = 0L;
            long endTime = 0L;
            StringBuffer buffer = new StringBuffer("");

            startTime = System.currentTimeMillis();
            for (int i = 0; i < 80000; i++) {
                buffer.append(String.valueOf(i));
            }
            endTime = System.currentTimeMillis();
            System.out.println("StringBuffer的執行時間:" + (endTime - startTime));





            StringBuilder builder = new StringBuilder("");
            startTime = System.currentTimeMillis();
            for (int i = 0; i < 80000; i++) {
                builder.append(String.valueOf(i));
            }
            endTime = System.currentTimeMillis();
            System.out.println("StringBuilder的執行時間:" + (endTime - startTime));


            String text = "";
            startTime = System.currentTimeMillis();
            for (int i = 0; i < 80000; i++) {
                text = text + i;
            }
            endTime = System.currentTimeMillis();
            System.out.println("String的執行時間:" + (endTime - startTime));

        }
    }
 這個是 i < 80000次的結果一篇講清楚String、StringBuffer和StringBuild

一篇講清楚String、StringBuffer和StringBuild

 這個是 i < 180000次的結果

一篇講清楚String、StringBuffer和StringBuild

可以發現如果次數不是很大StringBuffer和StringBuilder的差距還是可以的。次數越大差距越大。

 

四、總結:

String、StringBuffer、StringBuilder的選擇:

1、如果字串中存在大量的修改操作,可以選擇StrinBuffer和StringBuilder其中之一。

2、如果字串中存在大量的修改操作而且在單執行緒的情況下,使用StringBuilder。

3、如果字串中存在大量的修改操作而且在多執行緒的情況下,使用StringBuffer。

4、如果字串修改很少、被多個物件引用,使用String。這個在配置資訊的時候應用廣泛。

 

相關文章