Java Object類 和 String類 常見問答 6k字+總結

  • 2021 年 1 月 30 日
  • 筆記

寫在最前面

這個項目是從20年末就立好的 flag,經過幾年的學習,回過頭再去看很多知識點又有新的理解。所以趁著找實習的準備,結合以前的學習儲備,創建一個主要針對應屆生和初學者的 Java 開源知識項目,專註 Java 後端面試題 + 解析 + 重點知識詳解 + 精選文章的開源項目,希望它能伴隨你我一直進步!

說明:此項目我確實有很用心在做,內容全部是我參考了諸多部落客(已註明出處),資料,N本書籍,以及結合自己理解,重新繪圖,重新組織語言等等所制。個人之力綿薄,或有不足之處,在所難免,但更新/完善會一直進行。大家的每一個 Star 都是對我的鼓勵 !希望大家能喜歡。

註:所有涉及圖片未使用網路圖床,文章等均開源提供給大家。

項目名: Java-Ideal-Interview

Github 地址: Java-Ideal-Interview - Github

Gitee 地址:Java-Ideal-Interview - Gitee(碼雲)

持續更新中,在線閱讀將會在後期提供,若認為 Gitee 或 Github 閱讀不便,可克隆到本地配合 Typora 等編輯器舒適閱讀

若 Github 克隆速度過慢,可選擇使用中國 Gitee 倉庫

三 Java 常見對象

說明:本章主要涉及到了:Object類、Scanner類、String類、StringBuffer和StringBuilder、Arrays工具類、基本類型包裝類、正則表達式、System類、Math、Random類、BigInteger和BigDecimal類、Date、DateFormat和Calendar類

補充:由於 Object 以及 String 類屬於高頻內容,所以總結題目以及小點知識之前,會對其做一個基本的歸納複習。

1. 基本歸納

在講解這些常見類之前,我們不得不簡單的提一下什麼是API,先貼一組百度百科的解釋:

API(Application Programming Interface,應用程式編程介面)是一些預先定義的函數,目的是提供應用程式與開發人員基於某軟體或硬體得以訪問一組常式的能力,而又無需訪問源碼,或理解內部工作機制的細節。

簡單的說:就是 Java 中有好多現成的類庫,其中封裝了許多函數,只提供函數名和參數,但隱藏了函數的具體實現,這些可見的部分作為與外界聯繫的橋樑,也就是我們所稱的 API ,不過由於Java是開源的,所以這些隱藏的實現我們也是可以看到的。

1.1 Object 類

  • Object 是類層次結構的根類,所有的類都隱式的(不用寫extends)繼承自Object類。
  • Java 所有的對象都擁有Object默認方法
  • Object 類的構造方法有一個,並且是無參構造

這就對應了前面學習中的一句話,子類構造方法默認訪問父類的構造是無參構造

我們需要了解的方法又有哪些呢?

A:hashCode() B:getClass() C: finalize() D:clone() E:wait() F:notify() G:notifyAll()

我們需要掌握的方法又有哪些呢?

A:toString() B:equals()

方法總結:

// 1. 返回此Object的運行時類,是一個 native方法,同時因為使用了final關鍵字修飾,故不允許子類重寫。
public final native Class<?> getClass()

// 2. 用於返回對象的哈希碼,是一個native方法,例如主要涉及在 HashMap 中。
public native int hashCode() 
    
// 3. 比較兩個對象是否相同,默認比較的是地址值是否相同。而比較地址值是沒有意義的,所以,一般子類也會重寫該方法。
public boolean equals(Object obj)

// 4. 實現對象的克隆,包括成員變數的數據複製,分為深淺克隆兩種。是一個native方法。
protected native Object clone() throws CloneNotSupportedException

// 5. 返回類的名字@該實例16進位的哈希碼字元串。因此建議Object 所有的子類都重寫此方法。    
public String toString() 

// 6. 喚醒一個在此對象監視器上等待的執行緒(監視器理解為鎖)。若有多個執行緒在等待只會任意喚醒一個。是一個 native方法,且不能重寫。
public final native void notify()

// 7. 同 notify(),區別是會喚醒在此對象監視器上等待的所有執行緒。
public final native void notifyAll()

// 8. 意為暫停執行緒的執行.是一個native方法。注意:釋放了鎖,而sleep方法不釋放鎖。timeout是等待時間。
public final native void wait(long timeout) throws InterruptedException
    
// 9. 多了一個nanos參數,代表額外時間(以毫微秒為單位,範圍是 0-999999)。 所以時間最後要計算總和。
public final void wait(long timeout, int nanos) throws InterruptedException

// 10同前兩個 wait() 只不過該方法一直等待
public final void wait() throws InterruptedException

// 11. 在對象將被垃圾回收器清除前調用,但不確定時間  
protected void finalize() throws Throwable { }

2.2 String 類

String 是一個很常用的類,簡單歸納一下常見的方法

構造方法

// 1. 空構造
public String()

// 2. 把位元組數組轉換成字元串
public String(byte[] bytes)

// 3. 把位元組數組的一部分轉換成字元串
public String(byte[] bytes,int offset,int length)

// 4. 把字元數組轉換成字元串
public String(char[] value)

// 5. 把字元數組的一部分轉換成字元串
public String(char[] value,int offset,int count)

// 6. 把字元串常量值轉換成字元串    
public String(String original)

// 7. 下面的這一個雖然不是構造方法,但是結果也是一個字元串對象
String s = "hello";

簡單總結:String類的構造方法可以將 位元組、字元數組、字元串常量(全部或者部分)轉換為字元串類型

判斷方法

// 1. 比較字元串的內容是否相同,區分大小寫
boolean equals(Object obj)

// 2. 比較字元串的內容是否相同,不區分大小寫
boolean equalsIgnoreCase(String str)

// 3. 判斷大字元串中是否包含小字元串
boolean contains(String str)

// 4. 判斷某個字元串是否以某個指定的字元串開頭
boolean startsWith(String str)

// 5. 判斷某個字元串是否以某個指定的字元串結尾
boolean endsWith(String str)

// 6. 判斷字元串是否為空
boolean isEmpty()

注意:
String s = 「」;    // 字元串內容為空
String s = null;  // 字元串對象為空

獲取方法

// 1. 獲取字元串的長度
int length()

// 2. 獲取指定索引的字元
char charAt(int index)

// 3. 返回指定字元在此字元串中第一次出現的索引
int indexOf(int ch)
// 為什麼這裡是int而不是char?
// 原因是:『a』和『97』其實都能代表『a』 int方便

// 4. 返回指定字元串在此字元串中第一次出現的索引
int indexOf(String str)

// 5. 返回指定字元在此字元串中從指定位置後第一次出現的索引
int indexOf(int ch,int fromIndex)

// 6. 返回指定字元串在此字元串中從指定位置後第一次出現的索引
int indexOf(String str, int fromIndex)

// 7. 從指定位置開始截取字元串,默認到末尾
String substring(int start)

// 8. 從指定位置開始指定位置結束截取字元串
String substring(int start, int end)

轉換方法

// 1. 把字元串轉換為位元組數組
byte[] getBytes()

// 2. 把字元串轉換成字元數組
char[] toCharArray()

// 3. 把字元數組轉換成字元串
static String valueOf(char[] chs)

// 3. 把int類型的數據轉換成字元串
static String valueOf(int i)
// 注意:String類的valueOf方法可以把任何類型的數據轉換成字元串!
  
// 4. 把字元串轉換成小寫    
String toLowerCase()

// 5. 把字元串轉換成大寫
String toUpperCase()

// 7. 把字元串拼接
String concat(String str)

其他方法

// 1. 替換功能 
String replace(char old,char new)
String replace(String old,String new)

// 2. 去除字元串兩端空格
String trim()

// 3. 按字典比較功能
int compareTo(String str)
int compareToIgnoreCase(String str) 

2. 題目總結

2.1 == 和 equals 的區別?

== :如果比較的對象是基本數據類型,則比較的是數值是否相等;如果比較的是引用數據類型,則比較的是對象

的地址值是否相等。

equals():equals 方法不能用於基本數據類型的變數,如果沒有對 equals 方法進行重寫,則比較的是引用類型的變數所指向的對象的地址。一般會選擇重寫此方法,來比較兩個對象的內容是否相等,相等則返回 true。

2.2 如何比較兩個對象內容是否相同?(重寫 equals)

例如一個 Student 類,new 兩個對象出來,單純的想比較內容是否相同如何做呢。

public class Student {
    private String name;
    public int age;
    // get set ... 
}

通過 equals() 比較兩個對象是否相同,默認情況下,比較的是地址值是否相同。而比較地址值是沒有意義的,所以,一般子類也會重寫該方法。在諸多子類,如String、Integer、Date 等均重寫了 equals() 方法

改進思路:我們可以將比較地址值轉變為比較成員變數

  • 因為 name 為 String 類型,而 String 類型為引用類型,所以不能夠用 == 比較,應該用 equal()
  • String 中默認重寫過的 equal() 方法是用來比較字元串內容是否相同
  • 我們要使用的是學生類的成員變數,所以父類 Object不能調用子類Student的特有功能,所以使用向下轉型
//重寫v1.0
public boolean equals(Object o) {
    Student s = (Student) o;
    if (this.name.equals(s.name) && this.age == s.age) {
        return true;
    } else {
        return false;
    }
}
//重寫v2.0 (可作為最終版)
public boolean equals(Object o) {
    if (this.name == o) {
        return true;
    }
    //測試它左邊的對象是否是它右邊的類的實例,返回 boolean 的數據類型。
    if (!(o instanceof Student)) {
        return false;
    }
    Student s = (Student) o;
    return this.name.equals(s.name) && this.age == s.age;
}
// IDEA自動生成版
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Student student = (Student) o;
    return age == student.age &&
        Objects.equals(name, student.name);
}

2.3 hashCode() 和 equals()

2.3.1 什麼是 hashCode() 和 equals()

hashCode() 方法是 Object 類中的一個本地方法(用 c 語言或 c++ 實現的),會返回該對象的哈希碼,也稱為散列碼;其本質是返回一個 int 整數。哈希碼的作用是確定該對象在哈希表中的索引位置。可以通過散列碼,在散列表中根據「鍵」快速的檢索出對應的「值」。從而快速找到需要的對象,然後進行判斷是不是同一個對象。

public native int hashCode();

equals() 方法是Object 類中的一個方法,如果沒有對 equals 方法進行重寫,則比較的是引用類型的變數所指向的對象的地址。一般會選擇重寫此方法,來比較兩個對象的內容是否相等,相等則返回 true。

總結:單考慮目的兩者是差不多的,都是用來對比兩個對象是否相等一致。

2.3.2 equals() 已經實現功能了,還需要 hashCode() 做什麼?

重寫 equals() 裡面的內容一般比較全面周詳,但是效率就比較低,例如:如果集合中現在已經有2000個元素,那麼第2001個元素加入集合時,它就要調用 2000次 equals方法。

而使用 hashCode() ,其使用的哈希演算法也稱為散列演算法,是將數據依特定演算法直接指定到一個地址上,所以 hashCode() 這種形成 hash 碼的方式比較是比較高效的。

2.3.3 為什麼不全部使用高效率的 hashCode(),還要用 equals()?

hashCode() 方法不是一個 100% 可靠的方法,個別情況下,不同的對象生成的 hashcode 也可能會相同。

2.3.4 hashCode() 和 equals() 是如何一起判斷保證高效又可靠的?

如果大量內容都是用 equals() 去比對,效率顯然是比較低的,所以每次比對之前都去使用 hashCode() 去對比,如果返回的 hashCode 不同,代表兩個對象肯定不相同,就可以直接返回結果了。如果 hashCode 相同,又為了保證其絕對可靠,所以使用 equals() 再次進行比對,同樣是相同,就保證了這兩個對象絕對相同。

2.3.5 為什麼重寫 equals 時必須重寫 hashCode 方法?

如果重寫了 equals() 而未重寫 hashcode() 方法,可能就會出現兩個字面數據相同的對象(例如下面 stu1 和 stu2) equals 相同(因為 equals 都是根據對象的特徵進行重寫的),但 hashcode 不相同的情況。

public class Student {
    private String name;
    public int age;
    // get set ... 
    // 重寫 equals() 不重寫 hashcode()
}
--------------------------------------------
Student stu1 = new Student("BWH_Steven",22);
Student stu2 = new Student("BWH_Steven",22);
--------------------------------------------
stu1.equals(stu2); // true
stu1.hashCode();  // 和 stu2.hashCode(); 結果不一致
stu2.hashCode();

如果把對象保存到 HashTable、HashMap、HashSet 等中(不允許重複),這種情況下,去查找的時候,由於都是先使用 hashCode() 去對比,如果返回的 hashCode 不同,則會認為對象不同。可以存儲,從內容上看,明顯就重複了。

所以一般的地方不需要重寫 hashcode() ,只有當類需要放在 HashTable、HashMap、HashSet 等hash 結構的集合時才會去重寫。

補充:阿里巴巴 Java 開發手冊關於 hashCode 和 equals 的處理遵循規則:

  • 只要重寫 equals,就必須重寫 hashCode。
  • 因為 Set 存儲的是不重複的對象,依據 hashCode 和 equals 進行判斷,所以 Set 存儲的對象必須重寫這兩個方法。
  • 如果自定義對象做為 Map 的鍵,那麼必須重寫 hashCode 和 equals。
  • String 重寫了 hashCode 和 equals 方法,所以我們可以非常愉快地使用 String 對象作為 key 來使用。

2.4 深拷貝和淺拷貝的區別?

淺拷貝(淺克隆):基本數據類型為值傳遞,對象類型為引用傳遞(兩者同生共死)

深拷貝(深克隆):對於對象或者數值,所有元素或者屬性均完全複製,與原對象脫離(真正意義上的複製, 兩者獨立無關)

舉例:

public class Book {
    private String name; // 姓名
    private int price; // 價格
    private Partner partner; // 合作夥伴
    // 省略構造函數、get set、toString 等
}
public class Partner{
    private String name;
    
// 省略構造函數、get set、toString 等
}

淺拷貝用到拷貝,首先就對 Book 類進行處理

  • 首先實現 Cloneable 介面
  • 接著重寫 clone 方法
public class Book implements Cloneable{
    private String name; // 姓名
    private int price; // 價格
    private Partner partner; // 合作夥伴

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    // 省略構造函數、get set、toString 等
}

再來測試一下

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 初始化一個合作夥伴類型
        Partner partner = new Partner("張三");
        // 帶參賦值
        Book bookA = new Book("理想二旬不止", 66, partner);
        // B 克隆 A
        Book bookB = (Book) bookA.clone();

        System.out.println("A: " + bookA.toString());
        System.out.println("A: " + bookA.hashCode());
        System.out.println("B: " + bookB.toString());
        System.out.println("B: " + bookB.hashCode());
    }
}

執行結果

A: Book{name='理想二旬不止', price=66, partner=Partner{name=張三}}
A: 460141958
B: Book{name='理想二旬不止', price=66, partner=Partner{name=張三}}
B: 1163157884

結果非常明顯,書籍資訊是一致的,但是記憶體地址是不一樣的,也就是說確實克隆成功了,列印其 hashCode 發現兩者並不相同,說明不止指向同一個,也是滿足我們要求的

到這裡並沒有結束,你會發現還是有問題,當你刊印的過程中修改一些值的內容的時候,你看看效果

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 初始化一個合作夥伴類型
        Partner partner = new Partner("張三");
        // 帶參賦值
        Book bookA = new Book("理想二旬不止", 66, partner);
        // B 克隆 A
        Book bookB = (Book) bookA.clone();
        // 修改數據
        bookB.getPartner().setName("李四");
        bookB.setPrice(44);

        System.out.println("A: " + bookA.toString());
        System.out.println("A: " + bookA.hashCode());
        System.out.println("B: " + bookB.toString());
        System.out.println("B: " + bookB.hashCode());
    }
}

執行結果

A: Book{name='理想二旬不止', price=66, partner=Partner{name=李四}}
A: 460141958
B: Book{name='理想二旬不止', price=66, partner=Partner{name=李四}}
B: 1163157884

???這不對啊,B 明明是克隆 A 的,為什麼我在克隆後,修改了 B 中兩個值,但是 A 也變化了啊

這就是典型的淺克隆,在 Book 類,當欄位是引用類型,例如 Partner 這個合作夥伴類,就是我們自定義的類,這種情況不複製引用的對象,因此,原始對象和複製後的這個Partner對象是引用同一個對象的。而作為基本類型的的值就沒事。

如何解決上面的問題呢,我們需要重寫主類的 clone 的內容(改為深拷貝),同時在引用類型中也實現淺拷貝

A:被引用類型實現淺克隆

public class Partner implements Cloneable {
    private String name;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    // 省略構造函數、get set、toString 等
}

B:修改引用類 cloen 方法

public class Book implements Cloneable{
    private String name; // 姓名
    private int price; // 價格
    private Partner partner; // 合作夥伴

    @Override
    protected Object clone() throws CloneNotSupportedException {
		Object clone = super.clone();
        Book book = (Book) clone;
        book.partner =(Partner) this.partner.clone();
        return clone;
    }
	// 省略構造函數、get set、toString 等
}

C:測試一下

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 初始化一個合作夥伴類型
        Partner partner = new Partner("張三");
        // 帶參賦值
        Book bookA = new Book("理想二旬不止", 66, partner);
        // B 克隆 A
        Book bookB = (Book) bookA.clone();
        // 修改數據
        partner.setName("李四");

        System.out.println("A: " + bookA.toString());
        System.out.println("A: " + bookA.hashCode());
        System.out.println("B: " + bookB.toString());
        System.out.println("B: " + bookB.hashCode());
    }
}

執行效果

A: Book{name='理想二旬不止', price=66, partner=Partner{name=李四}}
A: 460141958
B: Book{name='理想二旬不止', price=66, partner=Partner{name=張三}}
B: 1163157884

可以看到,B 克隆 A 後,修改 A 中 合作夥伴 的值,沒有受到影響,這也就是我們通常意義上想要實現的效果了。

2.5 為什麼重寫 toString() 方法?

主要目的還是為了簡化輸出

  1. 在類中重寫toString()後,輸出類對象就變得有了意義(輸出s 和 s.toString()是一樣的 ,不寫也會默認調用),變成了我們實實在在的資訊 ,例如 Student{name='admin', age=20},而不是上面的 cn.ideal.pojo.Student@1b6d3586

  2. 如果我們想要多次輸出 類中的成員資訊,就需要多次書寫 ge t方法(每用一次就得寫)

toString() 方法,返回該對象的字元串表示。

Object 類的 toString 方法返回一個字元串,該字元串由類名(對象是該類的一個實例)at 標記符 @ 和此對象哈希碼的無符號十六進位表示組成。換句話說,該方法返回一個字元串,它的值等於:

程式碼:getClass().getName()+ '@' + Integer.toHexString(hashCode())

通常我們希望, toString 方法會返回一個「以文本方式表示」 此對象的字元串。結果應是一個簡明但易於讀懂的資訊表達式。因此建議所有子類都重寫此方法。

2.6 字元串使用 += 賦值後,原始的String對象中的內容會改變嗎?

答案:不會

/*
 *  字元串特點:一旦被賦值,就不能改變
 */
public class StringDemo {
    public static void main(String[] args) {
        String s = "Hello";
        s += "World";
        System.out.println("s:" + s);
    }
}

//運行結果:
s:HelloWorld

解釋:不能改變是指字元串對象本身不能改變,而不是指對象的引用不能改變,上述過程中,字元串本身的內容是沒有任何變化的,而是分別創建了三塊記憶體空間,(Hello) (World) (HelloWorld) Hello + World 拼接成 HelloWorld 這時,s 不指向原來那個 「Hello」 對象了,而指向了另一個String對象,內容為 「HelloWorld 」 ,原來那個對象還存在記憶體中,只是 s 這個引用變數不再指向它了。

總結:開發中,盡量少使用 + 進行字元串的拼接,尤其是循環內,我們更加推薦使用StringBuild、StringBuffer。

2.7 字元串構造函數賦值和直接賦值的區別?

通過 new 構造函數創建字元串對象。String s = new String("Hello"); 系統會先創建一個匿名對象 "Hello" 存入堆記憶體,而後 new 關鍵字會在堆記憶體中又開闢一塊新的空間,然後把"Hello"存進去,並且把地址返回給棧記憶體中的 s, 剛才的匿名對象 "Hello" 就變成了一個垃圾對象,因為它沒有被任何棧中的變數指向,會被GC自動回收。

直接賦值。如String str = "Hello"; 首先會去字元串常量池中找有沒有一個"Hello"對象,如果沒有,則新建一個,並且入池,所以此種賦值有一個好處,下次如果還有 String 對象也用直接賦值方式定義為「Hello」, 則不需要開闢新的堆空間,而仍然指向這個池中的"Hello"。

//兩者的區別
String s = new String("Hello");
String s = "Hello";

總結:前者new一個對象,「hello」隱式創建一個對象,後者只有「Hello」創建一個對象,在開發中,盡量使用 String s = "Hello" 的方式,效率比另一種高。

2.8 String、StringBuffer、StringBuilder的區別

前面我們用字元串做拼接,比較耗時並且也耗記憶體(每次都會構造一個新的string對象),而這種拼接操作又是比較常見的,為了解決這個問題,Java就提供了兩個字元串緩衝區類。StringBuffer和StringBuilder供我們使用。

簡單比較:

String:長度大小不可變

StringBuffer:長度可變、執行緒安全、速度較慢

StringBuilder:長度可變、執行緒不安全、速度最快

解釋:

  1. 在執行速度方面的比較:StringBuilder > StringBuffer

  2. StringBuffer與StringBuilder,他們是字元串變數,是可改變的對象,每當我們用它們對字元串做操作時,實際上是在一個對象上操作的,不像String一樣創建一些對象進行操作,所以速度就快了。

  3. StringBuilder:執行緒非安全的

    StringBuffer:執行緒是安全的(synchronized關鍵字進行修飾)

當我們在字元串緩衝區被多個執行緒使用時,JVM 不能保證 StringBuilder 的操作是安全的,雖然他的速度最快,但是可以保證 StringBuffer 是可以正確操作的。當然大多數情況下就是我們是在單執行緒下進行的操作,所以大多數情況下是建議用StringBuilder而不用StringBuffer的,就是速度的原因。

對於三者使用的總結:

  1. 如果要操作少量的數據用 String
  2. 單執行緒操作字元串緩衝區 下操作大量數據 StringBuilder
  3. 多執行緒操作字元串緩衝區 下操作大量數據 StringBuffer

2.9 字元串 「+」 和 StringBuilder 選擇用哪個?

首先java並不支援運算符重載(String類中的 「+」 和 「+=」 是 Java 中僅有的兩個重載過的運算符),所以我們可以通過 「+」 符號 將多個字元串進行拼接

將圖中程式碼(使用了 「+」 符號)利用 javap -c filename 反編譯

我們可以看到程式碼被編譯器自動優化成使用StringBuilder方式拼接,運行效率得到了保證

下面一個案例 數組拼接成指定格式的字元串 程式碼中使用了循環語句

// 在循環中通過String拼接字元串
public class StringBuilderDemo {
   public static void main(String[] args) {
       String[] arr = {"Hello", "World", "!!!"};
       String s1 = arrayToString(arr);
       System.out.println(s1);
   }

   public static String arrayToString(String[] arr) {
       String s = "";

       s += "[";
       for (int x = 0; x < arr.length; x++) {
           if (x == arr.length - 1) {
               s += arr[x];
           } else {
               s += arr[x];
               s += ", ";
           }
       }
       s += "]";
       return s;
   }
}
//運行結果
[Hello, World, !!!]

使用String方式進行拼接,我們反編譯可以看到,StringBuilder被創建在循環的內部,這意味著每循環一次就會創建一次StringBuilder對象,這可是一個糟糕的事情。

// 在循環中使用StringBuilder拼接字元串
public class StringBuilderDemo2 {
    public static void main(String[] args) {
        String[] arr = {"Hello", "World", "!!!"};
        String s1 = arrayToString(arr);
        System.out.println(s1);
    }

    public static String arrayToString(String[] arr) {
        StringBuilder s = new StringBuilder();
        s.append("[");
        for (int x = 0; x < arr.length; x++) {
            if (x == arr.length - 1) {
                s.append(arr[x]);
            } else {
                s.append(arr[x]);
                s.append(", ");
            }
        }
        s.append("]");
        return s.toString();
    }
}
//運行結果
[Hello, World, !!!]

使用StringBuilder方式進行拼接,自行去看一下彙編程式碼中,不僅循環部分的程式碼更為簡潔,而且它只生成了一個StringBuilder對象。顯式的創建StringBuilder對象還允許你預先為其指定大小。可以避免多次重新分配緩衝。

總結:

如果字元串操作比較簡單,就可以使用 「+」 運算符操作,編譯器會為你合理的構造出最終的字元串結果

如果使用循環語句 最好自己手動創建一個StringBuilder對象,用它來構最終結果

Tags: