轉自:http://www.cnblogs.com/bjut-xiaorun/p/5294811.html
1.不可變
String類初始化後是不可變的(immutable),首先,我建議先看看String類的源碼實現,這是從本質上認識String類的根本出發點。從中可以看到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public class StringTest
{ public static void main(String
args[]) { //在池中和堆中分別創建String對象"abc",s1指向堆中對象 String
s1 = new String( "abc" ); //s2直接指向池中對象"abc" String
s2 = "abc" ; //在堆中新創建"abc"對象,s3指向該對象 String
s3 = new String( "abc" ); //在池中創建對象"ab"
和 "c",並且s4指向池中對象"abc" String
s4 = "ab" + "c" ; //c指向池中對象"c" String
c = "c" ; //在堆中創建新的對象"abc",並且s5指向該對象 String
s5 = "ab" +
c; String
s6 = "ab" .concat( "c" ); String
s7 = "ab" .concat(c); System.out.println( "------------實串-----------" ); System.out.println(s1
== s2); //false System.out.println(s1
== s3); //false System.out.println(s2
== s3); //false System.out.println(s2
== s4); //true System.out.println(s2
== s5); //false System.out.println(s2
== s6); //false System.out.println(s2
== s7); //false } } |
2.使用String不一定創建對象
在執行到雙引號包含字符串的語句時,如String a = "123",JVM會先到常量池裏查找,如果有的話返回常量池裏的這個實例的引用,否則的話創建一個新實例並置入常量池裏。如果是 String a = "123" + b (假設b是"456"),前半部分"123"還是走常量池的路線,但是這個+操作符其實是轉換成[SringBuffer].Appad()來實現的,所以最終a得到是一個新的實例引用,而且a的value存放的是一個新申請的字符數組內存空間的地址(存放着"123456"),而此時"123456"在常量池中是未必存在的。
要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認爲,創建了String類的對象str。擔心陷阱!對象可能並沒有被創建!而可能只是指向一個先前已經創建的對象。只有通過new()方法才能保證每次都創建一個新的對象
3.使用new String,一定創建對象
在執行String a = new String("123")的時候,首先走常量池的路線取到一個實例的引用,然後在堆上創建一個新的String實例,走以下構造函數給value屬性賦值,然後把實例引用賦值給a
4.String.intern()
String對象的實例調用intern方法後,可以讓JVM檢查常量池,如果沒有實例的value屬性對應的字符串序列比如"123"(注意是檢查字符串序列而不是檢查實例本身),就將本實例放入常量池,如果有當前實例的value屬性對應的字符串序列"123"在常量池中存在,則返回常量池中"123"對應的實例的引用而不是當前實例的引用,即使當前實例的value也是"123"。
1
2
3
4
5
6
7
8
9
10
11
12
|
public static void main(String[]
args) { String
s0 = "kvill" ; String
s1 = new String( "kvill" ); String
s2 = new String( "kvill" ); System.out.println(
s0 == s1 ); //false System.out.println( "**********" ); s1.intern(); //雖然執行了s1.intern(),但它的返回值沒有賦給s1 s2
= s2.intern(); //把常量池中"kvill"的引用賦給s2 System.out.println(
s0 == s1); //flase System.out.println(
s0 == s1.intern() ); //true//說明s1.intern()返回的是常量池中"kvill"的引用 System.out.println(
s0 == s2 ); //true } |
5. String,StringBuffer與StringBuilder的區別
String:字符串常量,字符串長度不可變。Java中String是immutable(不可變)的。
StringBuffer:字符串變量(Synchronized,即線程安全)。如果要頻繁對字符串內容進行修改,出於效率考慮最好使用StringBuffer,如果想轉成String類型,可以調用StringBuffer的toString()方法。
StringBuilder:字符串變量(非線程安全)。在內部,StringBuilder對象被當作是一個包含字符序列的變長數組。
6 使用策略
(1)基本原則:如果要操作少量的數據,用String ;單線程操作大量數據,用StringBuilder ;多線程操作大量數據,用StringBuffer。
(2)不要使用String類的"+"來進行頻繁的拼接,因爲那樣的性能極差的,應該使用StringBuffer或StringBuilder類,這在Java的優化上是一條比較重要的原則。
(3)爲了獲得更好的性能,在構造 StirngBuffer 或 StirngBuilder 時應儘可能指定它們的容量。當然,如果你操作的字符串長度(length)不超過 16 個字符就不用了,當不指定容量(capacity)時默認構造一個容量爲16的對象。不指定容量會顯著降低性能。
(4)StringBuilder一般使用在方法內部來完成類似"+"功能,因爲是線程不安全的,所以用完以後可以丟棄。StringBuffer主要用在全局變量中。
(5)相同情況下使用 StirngBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風險。而在現實的模塊化編程中,負責某一模塊的程序員不一定能清晰地判斷該模塊是否會放入多線程的環境中運行,因此:除非確定系統的瓶頸是在 StringBuffer 上,並且確定你的模塊不會運行在多線程模式下,纔可以採用StringBuilder;否則還是用StringBuffer。