隨筆-126  評論-247  文章-5  trackbacks-0

          前言:
          字符串 (String) 是 java 編程語言中的核心類之一,在我們平常時候使用也比較很普遍,應用廣泛。
          但你是否知道什么是字符串直接量,知不知道有個字符串駐留池,字符串的駐留池可以用來緩存字符串直接量。


          什么是直接量
          直接量是指:在程序中,通過源代碼直接指定的值。
          eg:
          int personId = 8080 ;
          String name = "fancy" ;

          對于 java 中的字符串直接量,JVM 會使用一個字符串駐留池來緩存它們。一般情況下,字符串駐留池中的字符串對象不會被 GC (Garbage Collection,垃圾回收) 所回收,
          當再次使用字符串駐留池中已有的字符串對象時候,無需再次創建它,而直接使它的引用變量
          指向字符串駐留池中已有的字符串對象。

          String 基礎

          String 類代表字符串。字符串是常量,它們的值在創建之后是不能再被更改的。在 java 中除了 synchronized之外,不可變的類也是線程安全的,
          因此,String 類本身也是線程安全的。String 類的實例對象其實是可以被共享的。

          例代碼 

          1
          2        String name = "fancy";                  // @1
          3        String nick    = "fancydeepin"// @2
          4        name = nick;
          5        System.out.println(name);    // export:fancydeepin
          6


          結果輸出 :fancydeepin

          這是怎么回事?不是說 String 是不可變的字符串嗎?怎么這里又變了?
          是這樣的,在這里 name 只是一個引用類型變量,并不是一個 String 對象,@1中創建了一個 "fancy" 的字符串對象,
          @2中創建了一個 "fancydeepin" 的字符串對象,name 引用 (就像一個指針) 剛開始是指向 "fancy" 對象,而后,name 又重新指向 "fancydeepin" 對象,
          在示例代碼中,整個過程只創建了兩個 String 對象 (不知道我這樣說你能不能理解,為什么是只創建了兩個 String 對象?而不是 1個、3個...  @3),
          一個是 "fancy" 對象,另外一個是 "fancydeepin" 對象。而這兩個對象被創建出來后并沒有被改變過,之所以程序會輸出 fancydeepin,完全只是因為
          name 引用所指向的對象發生了改變。

          如果你是本著認真的態度看著我的貼子,細心的你,是否會留意到:
          當 name 引用重新指向另外一個對象的時候,那 name 之前引用的對象 ( "fancy" 對象 ) JVM 在底層會怎么處理它呢?是會立即來回收它來釋放系統資源嗎?
          答案是否定的。雖然這時候程序再也不訪問 "fancy" 這個對象,但 JVM 還是不會來回收它,它將在程序運行期間久駐內存,為什么會這樣呢?
          再往下說就扯到 java 的內存管理機制了,這里點到即止。在這里你可以簡單的將它理解成 "fancy" 對象被緩存了起來 ( 實際上也是因為被緩存了 )。

          字符串駐留池
          當比較兩個 String 對象時候,是應該用 "==" 呢?還是應該選擇 equals 呢?相信絕大部分人絕大多時候使用的都是選擇用 equals 方法。
          "==" 和 equals 的用法相信大家都很熟悉了,"==" 比較的是兩個對象的哈希碼值是否相等,而 equals 比較的是對象的內容是否一樣。
          而絕大部分時候我們比較兩個 String 對象的時候只是想比較它們的內容是否相等,這樣看來,只能選 equals 了,但真的是這樣嗎?
          答案是否定的。你一樣也可以用 "==" 來完成這樣的一件事情,而且 "==" 的效率無論如何都是要比使用 equals 的效率要高的,但前提是,
          需要使用字符串的駐留池,才能使用 "==" 來替代 equals 作比較。
          String 里面有個方法叫 intern(),執行效率很高,但也許你還不曾用過,下面是摘自API中 intern() 方法的描述:

          “當調用 intern 方法時,如果池已經包含一個等于此 String 對象的字符串(用 equals(Object) 方法確定),則返回池中的字符串。
          否則,將此 String 對象添加到池中,并返回此 String 對象的引用。
          它遵循以下規則:
          對于任意兩個字符串 st,當且僅當 s.equals(t)true 時,s.intern() == t.intern()
          才為 true。 ”

          例代碼:

           1
           2             String name = new String("fancy");
           3            
           4        if(name == "fancy") {    // false
           5            System.out.println("equals 1");
           6        }
          else {
           7            System.out.println("not equals 1");    // Be printed
           8        }

           9        
          10        name = name.intern();    // 將字符串添加到駐留池
          11        
          12        if(name == "fancy") {    // true
          13            System.out.println("equals 2");        // Be printed
          14        }
          else {
          15            System.out.println("not equals 2");
          16        }

          17


          輸出結果:

          1
          2not equals 1
          3equals 2
          4

           


          由上面的示例代碼可以看到,字符串駐留池的使用是非常簡單的,池中的對象可以被共享,只要你將字符串添加到池中,就能夠直接使用
          "==" 來比較兩個對象,而不是只能使用 equals 來作比較。將字符串添加到駐留池來使用 "==" 作比較的方式要比直接使用 equals 效率要高些。

          再論 String、StringBuffer StringBuilder

          由 synchronized 修飾的方法可以保證方法的線程安全,但是會降低該方法的執行效率;

          翻開 API,很容易就能知道:StringBuffer 是線程安全的可變字符序列,StringBuilder 是一個可變的字符序列,是線程不安全的;
          網上說的所謂的使用 StringBuffer 的效率更高更好,這已經不合時宜,這是 java 1.5 之前的版本的說法,早過時了現在!!
          現在是反過來了,由于 StringBuilder 不是線程安全的,StringBuilder 效率會比 StringBuffer 效率更高一些。

          你可以不相信我說的,但你總該相信程序跑出來的結果吧,下面是示例代碼:

           1
           2        StringBuffer  buffer  = new StringBuffer();
           3        StringBuilder builder = new StringBuilder();
           4        int COUNT = 10;       // 測試 COUNT 趟
           5        final int N = 100000// 每趟操作 N 次
           6        double beginTime, costTime; // 每趟開始時間和耗費時間
           7        double bufferTotalTime = 0.0D, buliderTotalTime = 0.0D// StringBuffer 和 StringBuilder 測試 COUNT 趟的總耗時
           8        while(COUNT -- > -1) {
           9            // 也可以測試每趟都創建一個新的對象,這樣 StringBuilder 效率比 StringBuffer 的效率變得更明顯了
          10            /**
          11            StringBuffer  buffer  = new StringBuffer();
          12            StringBuilder builder = new StringBuilder();
          13            */

          14            System.out.println("----------------------------------<" + (COUNT + 1+ ">");
          15            beginTime = System.currentTimeMillis();
          16            for(int i = 0; i < N; i++) {
          17                buffer.append(i);
          18                buffer.length();
          19            }

          20            costTime = System.currentTimeMillis() - beginTime;
          21            bufferTotalTime  += costTime;
          22            System.out.println("StringBuffer  費時: --->> " + costTime);
          23            beginTime = System.currentTimeMillis();
          24            for(int i = 0; i < N; i++) {
          25                builder.append(i);
          26                builder.length();
          27            }

          28            costTime = System.currentTimeMillis() - beginTime;
          29            buliderTotalTime += costTime;
          30            System.out.println("StringBuilder 費時: --->> " + costTime);
          31            System.out.println("----------------------------------<" + (COUNT + 1+ ">");
          32        }

          33        System.out.println("bufferTotalTime / buliderTotalTime = " + (bufferTotalTime / buliderTotalTime));
          34


          后臺輸出結果:

           1
           2----------------------------------<10>
           3StringBuffer  費時: --->> 32.0
           4StringBuilder 費時: --->> 16.0
           5----------------------------------<10>
           6----------------------------------<9>
           7StringBuffer  費時: --->> 21.0
           8StringBuilder 費時: --->> 15.0
           9----------------------------------<9>
          10----------------------------------<8>
          11StringBuffer  費時: --->> 25.0
          12StringBuilder 費時: --->> 35.0
          13----------------------------------<8>
          14----------------------------------<7>
          15StringBuffer  費時: --->> 24.0
          16StringBuilder 費時: --->> 8.0
          17----------------------------------<7>
          18----------------------------------<6>
          19StringBuffer  費時: --->> 48.0
          20StringBuilder 費時: --->> 38.0
          21----------------------------------<6>
          22----------------------------------<5>
          23StringBuffer  費時: --->> 22.0
          24StringBuilder 費時: --->> 8.0
          25----------------------------------<5>
          26----------------------------------<4>
          27StringBuffer  費時: --->> 23.0
          28StringBuilder 費時: --->> 9.0
          29----------------------------------<4>
          30----------------------------------<3>
          31StringBuffer  費時: --->> 25.0
          32StringBuilder 費時: --->> 7.0
          33----------------------------------<3>
          34----------------------------------<2>
          35StringBuffer  費時: --->> 23.0
          36StringBuilder 費時: --->> 7.0
          37----------------------------------<2>
          38----------------------------------<1>
          39StringBuffer  費時: --->> 78.0
          40StringBuilder 費時: --->> 59.0
          41----------------------------------<1>
          42----------------------------------<0>
          43StringBuffer  費時: --->> 21.0
          44StringBuilder 費時: --->> 11.0
          45----------------------------------<0>
          46bufferTotalTime / buliderTotalTime = 1.6056338028169015
          47


          StringBuffer 在測試中平均耗時是 StringBuilder 的 1.6 倍以上,再測多次,都是 1.6 倍以上。StringBuffer 和 StringBuilder 的性能孰更加優,一眼明了。

          最后,如果你想知道 @3 (在上面我已經用紅色粗體標出) 的結果,不妨說一下:
          eg:
          String mail = "fancydeepin" + "@" + "yeah.net";

          來,一起來看一下上面的這條語句,想一下,這條語句將會創建幾個 String 的對象呢?
          1個? 2個? 3個? 4個? 5個? ... ...

          也許你會認為是4個,它們分別是:"fancydeepin"、"@"、"yeah.net"、"fancydeepin@yeah.net"
          也許你會認為是5個,它們分別是:"fancydeepin"、"@"、"yeah.net"、"fancydeepin@"、"fancydeepin@yeah.net"
          也許 ... ...

          但實際上,這條語句只創建了一個 String 對象!
          為什么會這樣呢?原因很簡單,這是因為,mail 在 編譯的時候其值就已經確定,它就是 "fancydeepin@yeah.net" 。
          當程序處于運行期間且當上面的這條語句被執行到的時候,那么 mail 所引用的對象就會被創建,而 mail 的值由于在編譯的時候已經確定
          它是  "fancydeepin@yeah.net" ,所以最終只有一個 String 對象被創建出來,而這個對象就是  "fancydeepin@yeah.net" 對象。

          這樣解釋都能夠理解了吧?真的理解了嗎?是真的理解才好,不妨再來看一個:
          eg:
          String mail = new String("fancydeepin@yeah.net");

          這回又創建了幾個對象呢?
          答案是2個。為什么不是1個了呢?2個又是哪2個呢?
          可以很肯定的告訴你,它們分別是: "fancydeepin@yeah.net"、new String()
          這是因為,這回 mail 在編譯的時候它的值是還不能夠確定的,編譯只是將源代碼翻譯成字節碼,程序還并沒有跑起來,還 new 不了對象,
          所以在編譯完成之后,mail 的值是還不能夠確定的。
          當程序處于運行期間且當上面的這條語句被執行到的時候,這時候才開始去確定 mail 的引用對象,首先,"fancydeepin@yeah.net" 對象會被創建,
          之后,再執行 new String(),所以這條語句最后實際上是創建了 2個 String 對象。






           



            
          posted on 2012-07-09 01:07 fancydeepin 閱讀(2782) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 曲阜市| 遂平县| 大洼县| 浮山县| 辉南县| 昔阳县| 无为县| 望都县| 兴安县| 丰镇市| 颍上县| 麻城市| 壤塘县| 怀安县| 通榆县| 临高县| 郁南县| 镇安县| 壤塘县| 西充县| 老河口市| 历史| 海安县| 南京市| 多伦县| 两当县| 陵水| 山丹县| 梁平县| 茌平县| 盖州市| 沧州市| 云南省| 应城市| 罗城| 铜陵市| 德保县| 股票| 南召县| 甘肃省| 库车县|