String是什么
String字符串,是一種引用數據類型,並不是基礎數據類型。
對於基礎數據類型和引用數據類型的區別:
基礎數據類型,在創建時直接將值存放在棧內存中。
引用數據類型,在創建時棧內存中存放一個引用,這個引用存放的是堆內存的位置,而堆內存中就是存放具體的值。
舉例說明:
假如String對象是一個儲物櫃,在使用儲物櫃時(相當於新建一個String對象),
我們需要會得到一張記着儲物櫃的小票(小票相當於棧內存空間,小票上保存引用),
憑借這張小票就可以找到儲物櫃,然后拿到儲物櫃中存放的東西(儲物櫃相當於堆內存空間,可以根據引用去得到堆內存中的值)。
對於基礎數據類型,則是直接將值寫在小票上。
String的底層數據結構
String的底層實現是字符數組,這一點可以在源碼中看到,
其中屬性 private final char value[]; 就是用來存儲String的值,
以下是String類的部分源碼:
1 public final class String 2 implements java.io.Serializable, Comparable<String>, CharSequence { 3 /** The value is used for character storage. */ 4 private final char value[]; 5 6 /** Cache the hash code for the string */ 7 private int hash; // Default to 0 8 9 /** use serialVersionUID from JDK 1.0.2 for interoperability */ 10 private static final long serialVersionUID = -6849794470754667710L;
String類被final修飾,則表示String類不可以被繼承。
String類的value屬性使用了 final 進行修飾,則表示value是一個常量,常量不可以更改。
String對象的重新賦值
String類的源碼中可以看到String類的value屬性使用了 final 進行修飾,既然是常量。
在發生對象需要重新賦值的時候,String的做法是,將String對象的棧內存與堆內存的引用斷開。
再將棧內存中的引用指向新的堆內存空間。
本質上就是新建一個String對象。
由此可以引發一個問題,如果代碼中頻繁出現對String對象的重新賦值意味着會有大量的堆內存被棄用。
這部分被棄用的堆內存空間只能通過垃圾回收機制進行回收,這會降低內存的利用率。(浪費內存是一方面,另一方面垃圾回收也會耗費資源。此問題在面試中出現的概率比較高)
這個問題可以通過使用StringBuffer或者StringBuilder來解決(StringBuffer和StringBuilder會在接下來的文章進行介紹)。
以下是《Java開發實戰經典》一書中的示例:
String對象的比較
比較方式分為兩種(自定義比較方法除外):
a.雙等號(==):雙等號的判斷依據是對象的堆內存地址是否相同。(用上面儲物櫃的例子就是同一個儲物櫃,當然放的是同樣的東西)
b.使用方法equals(Object anObject):方法比較的是對象的取值。
String類中equals(Object anObject)方法源碼:
1 public boolean equals(Object anObject) { 2 if (this == anObject) { 3 return true; 4 } 5 if (anObject instanceof String) { 6 String anotherString = (String)anObject; 7 int n = value.length; 8 if (n == anotherString.value.length) { 9 char v1[] = value; 10 char v2[] = anotherString.value; 11 int i = 0; 12 while (n-- != 0) { //通過while循環比較字符數組中的值 13 if (v1[i] != v2[i]) 14 return false; 15 i++; 16 } 17 return true; 18 } 19 } 20 return false; 21 }
以下是代碼示例:
public class TestString { public static void main(String[] args) { String str1 = "a"; String str2 = "a"; String str3 = new String("a"); //使用new關鍵詞創建String對象 String str4 = "b"; System.out.println("str1 == str2: "+(str1 == str2)); System.out.println("str1 equals str2: "+(str1.equals(str2))); System.out.println("str1 == str3: "+(str1 == str3)); System.out.println("str1 equals str3: "+(str1.equals(str3))); System.out.println("str1 == str4: "+(str1 == str4)); } }
控制台結果:
str1 == str2: true str1 equals str2: true str1 == str3: false str1 equals str3: true str1 == str4: false
通過 str1 == str2: true 結果得出的結論:
String類在不使用new 關鍵詞來新建對象時,如果str1 和 str2 的取值相同,
並不會為str1 和 str2 分別開辟堆內存空間,而是將str1 和 str2同時指向同一個堆內存,減少內存浪費。
String類常用方法
以下是《Java開發實戰經典》一書中表格:
結語
紙上得來終覺淺,絕知此事要躬行。
String類中的內容很多,單構造方法就是十多種。
雖然內容多,但是難度都不高可能花看一個小視頻的時間能看完。
這里對String類進行簡單的介紹。