Java String類詳解(一)
String類是一個字串型別的類,使用“XXXX”定義的內容都是字串,雖然這個類在使用上有一些特殊,但是String本身是一個類。
一、String的例項化兩種方式
1、直接賦值例項化:
String StringName= "xxx";
以上是String物件的直接賦值,以上的程式碼並沒有使用關鍵字new進行。String類也是類,所以也有構造方法。
2、使用構造方法例項化:
public String(String str);
可以通過構造方法為String類物件例項化,但在構造裡面依然要接收一個本類物件。
二、字串的比較
如果要想知道兩個int型變數是否相等,使用“==”進行驗證,在String中也可以使用“==”來進行比較。我們來看一個例子:
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello" ;
String str2 = new String("Hello") ;
String str3 = str2 ; // 引用傳遞
System.out.println(str1 == str2) ;
System.out .println(str1 == str3) ;
System.out.println(str2 == str3) ;
}
}
執行結果:
false
false
true
發現使用“==”好象最終的判斷結果是不一樣的,為什麼呢?下面通過記憶體關係圖來分析:
通過以上分析可以發現,“==”比較的不是字串物件包含的內容,而是兩個物件所在的的記憶體物件的數值。所以“==”屬於數值比較,比較的是記憶體地址。
如果想比較字串的內容,可以使用String類的equals()方法。
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello" ;
String str2 = new String("Hello") ;
String str3 = str2 ; // 引用傳遞
System.out.println(str1.equals(str2)) ;
System.out.println(str1.equals(str3)) ;
System.out.println(str2.equals(str3)) ;
}
}
執行結果:
true
true
true
於是,現在比較的不是字串的記憶體地址的數值,而是字串的內容。
小結:
(1) ==:比較的是兩個字串記憶體地址的數值是否相等,屬於數值比較;
(2)equals():比較的是兩個字串的內容,屬於內容比較。
三、字串常量是匿名物件
在各個語言中並沒有提供字串的資料型別定義,很多語言都是使用字元陣列來描述字串的概念,在Java中也沒有字串的概念,只是Java自己做了簡單處理。但是在Java中字串依然不屬於基本資料型別,字串是作為String類的匿名物件的形式存在的。
字串是匿名物件的驗證:
public class StringDemo {
public static void main(String args[]) {
String str = "Hello" ;
// 通過字串呼叫方法
System.out.println("Hello".equals(str)) ;
}
}
執行結果:
true
匿名物件可以呼叫類中的方法與屬性,而以上的字串呼叫了equals()方法,所以它一定是一個物件。
四、String類物件兩種例項化方式的區別
String類物件存在兩種例項化的操作形式,那麼這兩種有什麼區別,在開發之中應該使用那一種更好呢?
1、直接賦值的例項化方式:
String str = "Hello" ;
此時,只分配了一塊堆記憶體空間和一塊棧記憶體空間:
再看一下程式碼:
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello" ;
String str2 = "Hello" ;
String str3 = "Hello" ;
System.out.println(str1 == str2) ;
System.out.println(str1 == str3) ;
System.out.println(str2 == str3) ;
}
}
執行結果:
true
true
true
我們發現以上所有直接賦值的String類物件的記憶體地址完全相同,記憶體分配圖如下:
在設計String類的時候採用了一種稱為共享設計模式的概念。在執行的JVM底層存在一個字串的物件池(Object Pool),如果使用者採用了直接賦值的方式時,會將字串的內容放入池儲存,如果以後其他String物件繼續使用直接賦值方式例項化,並且設定了同樣的內容時,那麼將不會分配新的堆記憶體空間,而是使用已有物件的引用進行分配繼續使用。如果新宣告的字串內容不在物件池中,則會分配一個新的,然後繼續放到池中以供下次使用。
2、採用構造方法例項化的方式:
使用構造方法例項化一定要用到new關鍵字,而一旦使用了new就表示要分配新的記憶體空間。
String str = new String("Hello") ;
記憶體分配圖如下:
從上可以發現,分配了兩塊堆記憶體空間,其中一塊是垃圾。這樣處理記憶體的浪費外,使用構造方法定義的String類物件,其內容不會儲存在物件中(因為重新分配了新的一塊堆記憶體)。
現在希望使用構造方法定義的String類物件,其內容要儲存在物件中,該怎麼辦麼?我們可以使用String類定義的一個手工入池的方法:
public String intern()
範例如下:
public class StringDemo {
public static void main(String args[]) {
String str1 = new String("Hello").intern() ;
String str2 = "Hello" ; // 入池
String str3 = "Hello" ; // 使用池物件
System.out.println(str1 == str2) ;
System.out.println(str1 == str3) ;
System.out.println(str2 == str3) ;
}
}
執行結果:
true
true
true
小結:String類物件兩種例項化的區別?
(1)直接賦值例項化方式(String str = “xxx”):只會分配一塊堆記憶體空間,並且物件內容自動入池,以供重複使用;
(2)構造方法例項化方式(String str = new String(“xxx”)):會分配兩塊堆記憶體空間,其中有一塊是垃圾,並且不會自動入池,使用者可以使用intern()方法手動入池。
五、字串的內容一旦定義則不可改變
先看一段程式碼:
public class StringDemo {
public static void main(String args[]) {
String str = "Hello " ;
str += "World " ;
str = str + "!!!" ;
System.out.println(str) ;
}
}
執行結果:
Hello World !!!
我們通過記憶體分配圖分析一下:
通過以上的分析可以發現:字串內容的更改,實際上改變的是字串物件的引用過程,並且會伴隨有大量的垃圾出現,在實際開發中應該避免。