java編程思想-字符串

轉自:http://www.cnblogs.com/bjut-xiaorun/p/5294811.html

1.不可變

String類初始化後是不可變的(immutable),首先,我建議先看看String類的源碼實現,這是從本質上認識String類的根本出發點。從中可以看到:

1、String類是final的,不可被繼承。public final class String。
2、String類是的本質是字符數組char[], 並且其值不可改變。private final char value[];
然後打開String類的API文檔,可以發現:
3、String類對象有個特殊的創建的方式,就是直接指定比如String x = "abc","abc"就表示一個字符串對象。而x是"abc"對象的地址,也叫做"abc"對象的引用。
4、String對象可以通過“+”串聯。串聯後會生成新的字符串。也可以通過concat()來串聯,這個後面會講述。
6、Java運行時會維護一個String Pool(String池),JavaDoc翻譯很模糊“字符串緩衝區”。String池用來存放運行時中產生的各種字符串,並且池中的字符串的內容不重複。而一般對象不存在這個緩衝池,並且創建的對象僅僅存在於方法的堆棧區。
 
String對象的創建也很講究,關鍵是要明白其原理。
原理1:當使用任何方式來創建一個字符串對象s時,Java運行時(運行中JVM)會拿着這個X在String池中找是否存在內容相同的字符串對象,如果不存在,則在池中創建一個字符串s,否則,不在池中添加。
 
原理2:Java中,只要使用new關鍵字來創建對象,則一定會(在堆區或棧區)創建一個新的對象。
 
原理3:使用直接指定或者使用純字符串串聯來創建String對象,則僅僅會檢查維護String池中的字符串,池中沒有就在池中創建一個,有則罷了!但絕不會在堆棧區再去創建該String對象。
 
原理4:使用包含變量的表達式來創建String對象,則不僅會檢查維護String池,而且還會在堆棧區創建一個String對象。
 
另外,String的intern()方法是一個本地方法,定義爲public native String intern(); intern()方法的價值在於讓開發者能將注意力集中到String池上。當調用 intern 方法時,如果池已經包含一個等於此 String 對象的字符串(該對象由 equals(Object) 方法確定),則返回池中的字符串。否則,將此 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。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章