<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-0  評論-1  文章-13  trackbacks-0

    String的創(chuàng)建

      String s = "hello";
      JVM先根據(jù)內(nèi)容"hello"查找對象,如果沒有找到,則在heap上創(chuàng)建新對象,并將其賦予s1,否則使用已經(jīng)存在的對象

      String s = new String("hello");
      JVM直接在heap上創(chuàng)建新的對象,所以在heap中會出現(xiàn)內(nèi)容相同,地址不同的String對象


    String的比較

      "=="        比較地址
      "equals"    比較內(nèi)容

      舉例:
      String s1 = "hello";
      String s2 = "hello";
      String s3 = new String("hello");

      s1 == s2;               // true        地址相同
      s1 == s3;               // false       地址不同
      s1.equals(s2);          // true        內(nèi)容相同
      s1.equals(s3);          // true        內(nèi)容相同


    intern() 方法

       查找內(nèi)容相同(equals())的字符串

       String s1 = "hello";                // hello不存在,jvm創(chuàng)建新對象 (1)
       String s2 = new String("hello");    // 創(chuàng)舉新對象 (2),這時heap中存在兩個內(nèi)容為hello的對象
       s1 == s2;           // false        // 地址不同
       s1.equals(s2);      // true         // 內(nèi)容相同
       s2 = s2.intern();   // true         // 找到對象(1) 并賦予s2
       s1 == s2;           // true !!      // 注意:此時s1,s2同指向(1)


    效率:String 與 StringBuffer

        情景1:
        (1) String result = "hello" + " world";
        (2) StringBuffer result = new String().append("hello").append(" world");

            (1) 的效率好于 (2),不要奇怪,這是因為JVM會做如下處理
            編譯前   String result = "hello" + " world";
            編譯后   String result = "hello world";


        情景2:
        (1) public String getString(String s1, String s2) {
                return s1 + s2;
            }
        (2) public String getString(String s1, String s2) {
                return new StringBuffer().append(s1).append(s2);
            }

            (1) 的效率與 (2) 一樣,這是因為JVM會做如下處理
            編譯前   return s1 + s2;
            編譯后   return new StringBuffer().append(s1).append(s2);


        情景3:
        (1) String s = "s1";
                  s += "s2";
                  s += "s3";
        (2) StringBuffer s = new StringBuffer().append("s1").append("s2").append("s3");

            (2) 的效率好于(1),因為String是不可變對象,每次"+="操作都會造成構(gòu)造新的String對象


        情景4:
        (1) StringBuffer s = new StringBuffer();
            for (int i = 0; i < 50000; i ++) {
                s.append("hello");
            }
        (2) StringBuffer s = new StringBuffer(250000);
            for (int i = 0; i < 50000; i ++) {
                s.append("hello");
            }
          
            (2) 的效率好于 (1),因為StringBuffer內(nèi)部實現(xiàn)是char數(shù)組,默認(rèn)初始化長度為16,每當(dāng)字符串長度大于char
            數(shù)組長度的時候,JVM會構(gòu)造更大的新數(shù)組,并將原先的數(shù)組內(nèi)容復(fù)制到新數(shù)組,(2)避免了復(fù)制數(shù)組的開銷
           
       


    關(guān)鍵點
        1). 簡單的認(rèn)為 .append() 效率好于 "+" 是錯誤的!
        2). 不要使用 new 創(chuàng)建 String
        3). 注意 .intern() 的使用
        4). 在編譯期能夠確定字符串值的情況下,使用"+"效率最高
        5). 避免使用 "+=" 來構(gòu)造字符串
        6). 在聲明StringBuffer對象的時候,指定合適的capacity,不要使用默認(rèn)值(18)
        7). 注意以下二者的區(qū)別不一樣
            - String s = "a" + "b";
            - String s = "a";
              s += "b";

     

    String和StringBuffer之概覽
      創(chuàng)建字符串的較佳途徑
      滯留字符串帶來的優(yōu)化
      連接字符串時的優(yōu)化技巧
      借助StringBuffer的初始化過程的優(yōu)化技巧
      關(guān)鍵點

     
    String和StringBuffer之概覽
      非可變對象一旦創(chuàng)建之后就不能再被改變,可變對象則可以在創(chuàng)建之后被改變。String對象是非可變對象,StringBuffer對象則是可變對象。為獲得更佳的性能你需要根據(jù)實際情況小心謹(jǐn)慎地選擇到底使用這兩者中的某一個。下面的話題會作詳細(xì)的闡述。(注意:這個章節(jié)假設(shè)讀者已經(jīng)具備Java的String和StringBuffer的相關(guān)基礎(chǔ)知識。)
     
    創(chuàng)建字符串的較佳途徑
    你可以按照以下方式創(chuàng)建字符串對象:
    1. String s1 = "hello"; 
        String s2 = "hello"; 
    2. String s3 = new String("hello");
        String s4 = new String("hello");
     
    上面哪種方式會帶來更好的性能呢?下面的代碼片斷用來測量二者之間的區(qū)別。

    StringTest1.java
    package com.performance.string;
    /** This class shows the time taken for creation of
     *  String literals and String objects.
     */
    public class StringTest1 {
    public static void main(String[] args){
        // create String literals
        long startTime = System.currentTimeMillis();
        for(int i=0;i<50000;i++){
        String s1 = "hello";
        String s2 = "hello";
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Time taken for creation of String literals : "
                      + (endTime - startTime) + " milli seconds" );
        // create String objects using 'new' keyword       
        long startTime1 = System.currentTimeMillis();
        for(int i=0;i<50000;i++){
        String s3 = new String("hello");
        String s4 = new String("hello");
        }
        long endTime1 = System.currentTimeMillis();
        System.out.println("Time taken for creation of String objects : "
                      + (endTime1 - startTime1)+" milli seconds");
        }
    }

    這段代碼的輸出:
    Time taken for creation of String literals : 0 milli seconds
    Time taken for creation of String objects : 170 milli seconds
     
    JVM是怎樣處理字符串的呢?
      Java虛擬機(jī)會維護(hù)一個內(nèi)部的滯留字符串對象的列表(唯一字符串的池)來避免在堆內(nèi)存中產(chǎn)生重復(fù)的String對象。當(dāng)JVM從class文件里加載字符串字面量并執(zhí)行的時候,它會先檢查一下當(dāng)前的字符串是否已經(jīng)存在于滯留字符串列表,如果已經(jīng)存在,那就不會再創(chuàng)建一個新的String對象而是將引用指向已經(jīng)存在的String對象,JVM會在內(nèi)部為字符串字面量作這種檢查,但并不會為通過new關(guān)鍵字創(chuàng)建的String對象作這種檢查。當(dāng)然你可以明確地使用String.intern()方法強(qiáng)制JVM為通過new關(guān)鍵字創(chuàng)建的String對象作這樣的檢查。這樣可以強(qiáng)制JVM檢查內(nèi)部列表而使用已有的String對象。
      所以結(jié)論是,JVM會內(nèi)在地為字符串字面量維護(hù)一些唯一的String對象,程序員不需要為字符串字面量而發(fā)愁,但是可能會被一些通過new關(guān)鍵字創(chuàng)建的String對象而困擾,不過他們可以使用intern()方法來避免在堆內(nèi)存上創(chuàng)建重復(fù)的String對象來改善Java的運(yùn)行性能。下一小節(jié)會向大家展示更多的信息。
     
    下圖展示了未使用intern()方法來創(chuàng)建字符串的情況。
     

      你可以自己使用==操作符和String.equals()方法來編碼測試上面提到的區(qū)別。==操作符會返回true如果一些引用指向一個相同的對象但不會判斷String對象的內(nèi)容是否相同;String.equals()方法會返回true如果被操作的String對象的內(nèi)容相同。對于上面的代碼會有s1==s2,因為s1和s2兩個引用指向同一個對象,對于上面的代碼,s3.equals(s4)會返回true因為兩個對象的內(nèi)容都一樣為”hello”。你可以從上圖看出這種機(jī)制。在這里有三個獨立的包含了相同的內(nèi)容(”hello”)的對象,實際上我們不需要這么三個獨立的對象――因為要運(yùn)行它們的話既浪費(fèi)時間又浪費(fèi)內(nèi)存。
     
      那么怎樣才能確保String對象不會重復(fù)呢?下一個話題會涵蓋對于內(nèi)建String機(jī)制的興趣。
     
    滯留字符串的優(yōu)化作用
      同一個字符串對象被重復(fù)地創(chuàng)建是不必要的,String.intern()方法可以避免這種情況。下圖說明了String.intern()方法是如何工作的,String.intern()方法檢查字符串對象的存在性,如果需要的字符串對象已經(jīng)存在,那么它會將引用指向已經(jīng)存在的字符串對象而不是重新創(chuàng)建一個。下圖描繪了使用了intern()方法的字符串字面量和字符串對象的創(chuàng)建情況。
     
    下面的例程幫助大家了解String.intern()方法的重要性。
    StringTest2.java
     
    package com.performance.string;
    // This class shows the use of intern() method to improve performance
    public class StringTest2 {
    public static void main(String[] args){
        // create String references like s1,s2,s3...so on..
        String variables[] = new String[50000];
        for( int i=0;i         variables[i] = "s"+i;
        }
        // create String literals
        long startTime0 = System.currentTimeMillis();
        for(int i=0;i         variables[i] = "hello";
        }
        long endTime0 = System.currentTimeMillis();
        System.out.println("Time taken for creation of String literals : "
                             + (endTime0 - startTime0) + " milli seconds" );
        // create String objects using 'new' keyword       
        long startTime1 = System.currentTimeMillis();
        for(int i=0;i         variables[i] = new String("hello");
        }
        long endTime1 = System.currentTimeMillis();
        System.out.println("Time taken for creation of String objects with 'new' key word : "
                            + (endTime1 - startTime1)+" milli seconds");
        // intern String objects with intern() method   
        long startTime2 = System.currentTimeMillis();
        for(int i=0;i         variables[i] = new String("hello");
            variables[i] = variables[i].intern();
        }
        long endTime2 = System.currentTimeMillis();
        System.out.println("Time taken for creation of String objects with intern(): "
                            + (endTime2 - startTime2)+" milli seconds");
        }
    }
    這是上面那段代碼的輸出結(jié)果:
    Time taken for creation of String literals : 0 milli seconds
    Time taken for creation of String objects with 'new' key word : 160 milli seconds
    Time taken for creation of String objects with intern(): 60 milli seconds
     
    連接字符串時候的優(yōu)化技巧
      你可以使用+操作符或者String.concat()或者StringBuffer.append()等辦法來連接多個字符串,那一種辦法具有最佳的性能呢?
      如何作出選擇取決于兩種情景,第一種情景是需要連接的字符串是在編譯期決定的還是在運(yùn)行期決定的,第二種情景是你使用的是StringBuffer還是String。通常程序員會認(rèn)為StringBuffer.append()方法會優(yōu)于+操作符或String.concat()方法,但是在一些特定的情況下這個假想是不成立的。
     
    1) 第一種情景:編譯期決定相對于運(yùn)行期決定
    請看下面的StringTest3.java代碼和輸出結(jié)果。

    package com.performance.string;
    /** This class shows the time taken by string concatenation at compile time and run time.*/
    public class StringTest3 {
      public static void main(String[] args){
        //Test the String Concatination
        long startTime = System.currentTimeMillis();
        for(int i=0;i<5000;i++){
        String result = "This is"+ "testing the"+ "difference"+ "between"+
                "String"+ "and"+ "StringBuffer";
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Time taken for string concatenation using + operator : "
             + (endTime - startTime)+ " milli seconds");
        //Test the StringBuffer Concatination
        long startTime1 = System.currentTimeMillis();
        for(int i=0;i<5000;i++){
        StringBuffer result = new StringBuffer();
             result.append("This is");
            result.append("testing the");
            result.append("difference");
            result.append("between");
           result.append("String");
           result.append("and");
           result.append("StringBuffer");
         }
        long endTime1 = System.currentTimeMillis();
        System.out.println("Time taken for String concatenation using StringBuffer : "
               + (endTime1 - startTime1)+ " milli seconds");
      }
    }
    這是上面的代碼的輸出結(jié)果:
    Time taken for String concatenation using + operator : 0 milli seconds
    Time taken for String concatenation using StringBuffer : 50 milli seconds

    很有趣地,+操作符居然比StringBuffer.append()方法要快,為什么呢?
     
      這里編譯器的優(yōu)化起了關(guān)鍵作用,編譯器像下面舉例的那樣簡單地在編譯期連接多個字符串。它使用編譯期決定取代運(yùn)行期決定,在你使用new關(guān)鍵字來創(chuàng)建String對象的時候也是如此。
     
    編譯前:
    String result = "This is"+"testing the"+"difference"+"between"+"String"+"and"+"StringBuffer";
    編譯后:
    String result = "This is testing the difference between String and StringBuffer";

    這里String對象在編譯期就決定了而StringBuffer對象是在運(yùn)行期決定的。運(yùn)行期決定需要額外的開銷當(dāng)字符串的值無法預(yù)先知道的時候,編譯期決定作用于字符串的值可以預(yù)先知道的時候,下面是一個例子。
     
    編譯前:
    public String getString(String str1,String str2) {
        return str1+str2;
    }
    編譯后:
    return new StringBuffer().append(str1).append(str2).toString();
    運(yùn)行期決定需要更多的時間來運(yùn)行。
     
    2) 第二種情景:使用StringBuffer取代String
    看看下面的代碼你會發(fā)現(xiàn)與情景一相反的結(jié)果――連接多個字符串的時候StringBuffer要比String快。
    StringTest4.java
     
    package com.performance.string;
    /** This class shows the time taken by string concatenation
    using + operator and StringBuffer  */
    public class StringTest4 {
     public static void main(String[] args){
        //Test the String Concatenation using + operator
        long startTime = System.currentTimeMillis();
        String result = "hello";
        for(int i=0;i<1500;i++){
            result += "hello";
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Time taken for string concatenation using + operator : "
                      + (endTime - startTime)+ " milli seconds");
        //Test the String Concatenation using StringBuffer
        long startTime1 = System.currentTimeMillis();
        StringBuffer result1 = new StringBuffer("hello");
        for(int i=0;i<1500;i++){
            result1.append("hello");
        }
        long endTime1 = System.currentTimeMillis();
        System.out.println("Time taken for string concatenation using StringBuffer :  "
                      + (endTime1 - startTime1)+ " milli seconds");
        }
    }
    這是上面的代碼的輸出結(jié)果:
    Time taken for string concatenation using + operator : 280 milli seconds
    Time taken for String concatenation using StringBuffer : 0 milli seconds
    看得出StringBuffer.append()方法要比+操作符要快得多,為什么呢?

      原因是兩者都是在運(yùn)行期決定字符串對象,但是+操作符使用不同于StringBuffer.append()的規(guī)則通過String和StringBuffer來完成字符串連接操作。(譯注:什么樣的規(guī)則呢?)
     
    借助StringBuffer的初始化過程的優(yōu)化技巧
      你可以通過StringBuffer的構(gòu)造函數(shù)來設(shè)定它的初始化容量,這樣可以明顯地提升性能。這里提到的構(gòu)造函數(shù)是StringBuffer(int length),length參數(shù)表示當(dāng)前的StringBuffer能保持的字符數(shù)量。你也可以使用ensureCapacity(int minimumcapacity)方法在StringBuffer對象創(chuàng)建之后設(shè)置它的容量。首先我們看看StringBuffer的缺省行為,然后再找出一條更好的提升性能的途徑。
     
    StringBuffer的缺省行為:
      StringBuffer在內(nèi)部維護(hù)一個字符數(shù)組,當(dāng)你使用缺省的構(gòu)造函數(shù)來創(chuàng)建StringBuffer對象的時候,因為沒有設(shè)置初始化字符長度,StringBuffer的容量被初始化為16個字符,也就是說缺省容量就是16個字符。當(dāng)StringBuffer達(dá)到最大容量的時候,它會將自身容量增加到當(dāng)前的2倍再加2,也就是(2*舊值+2)。
      如果你使用缺省值,初始化之后接著往里面追加字符,在你追加到第16個字符的時候它會將容量增加到34(2*16+2),當(dāng)追加到34個字符的時候就會將容量增加到70(2*34+2)。無論何事只要StringBuffer到達(dá)它的最大容量它就不得不創(chuàng)建一個新的字符數(shù)組然后重新將舊字符和新字符都拷貝一遍――這也太昂貴了點。所以總是給StringBuffer設(shè)置一個合理的初始化容量值是錯不了的,這樣會帶來立竿見影的性能增益。
      我利用兩個StringBuffer重新測試了上面的StringTest4.java代碼,一個未使用初始化容量值而另一個使用了。這次我追加了50000個’hello’對象沒有使用+操作符。區(qū)別是我使用StringBuffer(250000)的構(gòu)造函數(shù)來初始化第二個StringBuffer了。
     
    輸出結(jié)果如下:
    Time taken for String concatenation using StringBuffer with out setting size: 280 milli seconds
    Time taken for String concatenation using StringBuffer with setting size: 0 milli seconds
    StringBuffer初始化過程的調(diào)整的作用由此可見一斑。所以,使用一個合適的容量值來初始化StringBuffer永遠(yuǎn)都是一個最佳的建議。
     
    關(guān)鍵點
    1. 無論何時只要可能的話使用字符串字面量來常見字符串而不是使用new關(guān)鍵字來創(chuàng)建字符串。
    2. 無論何時當(dāng)你要使用new關(guān)鍵字來創(chuàng)建很多內(nèi)容重復(fù)的字符串的話,請使用String.intern()方法。
    3. +操作符會為字符串連接提供最佳的性能――當(dāng)字符串是在編譯期決定的時候。
    4. 如果字符串在運(yùn)行期決定,使用一個合適的初期容量值初始化的StringBuffer會為字符串連接提供最佳的性能。
    String與StringBuffer的比較
    String類提供了一些方法,用來進(jìn)行字符串的比較。這個類實現(xiàn)了Object父類的equals()方法,用來比較兩種字符串的值是否相等。同時還增加了equalsIgnoreCase()方法可以忽略兩個字符串大小寫的區(qū)別。下面是這兩種方法的例子。
    【例6-6】
    public class E6_6{
     public static void main(String args[]) {
      String s1="a";
      String s2=new String("a");
      String s3="A";
      System.out.println(s1.equals(s2));
      System.out.println(s1.equals(s3));
      System.out.println(s1.equalsIgnoreCase(s3));
     }
    }
    上例的輸出是
    true
    flase
    true

    但是StringBuffer類并沒有實現(xiàn)Objcet類的Equals方法,所以不能用這個方法來比較兩個StringBuffer類的字符串是否相等,如下例所示。
    【例6-7】
    public class E6_7{
     public static void main(String args[]) {
      StringBuffer s1=new StringBuffer("a");
      StringBuffer s2=new StringBuffer("a");
      System.out.println(s1.equals(s2));
      
     }
    }
    程序輸出:false

    除了用equalse方法來比較兩個字符串外,還可以用==來比較字符串。與equalse方法不同的是,==不是比較兩個字符串的值是否相等,而是比較幾個字符串的引用是否指向同一個實例。如例6-8所示。
    【例6-8】
    public class E6_8{
     public static void main(String args[]) {
      String s1="a";
      String s2="a";
      String s3=new String("a");
      String s4=new String("a");
      System.out.println(s1==s2);
      System.out.println(s3==s4);
      System.out.println(s1==s3);
      
     }
     
    }
    上面的程序段輸出:
    true
    false
    false
    與上例進(jìn)行比較,不僅可以看出來==與equals的區(qū)別,還可以看到字面量的String的特殊之外。
    對于字面量的String,只要字符串的值是相等的,不論有多少個引用都是指向同一塊內(nèi)存,不再另外分配空間。而用new關(guān)鍵字生成的實例則不同,每當(dāng)用new實例化一次,分配該實例自己的內(nèi)存空間。上例的存儲方式如圖所示:

     
    圖6-1  s1、s2、s3、s4的區(qū)別
    下面再通過另一個例子來看String和StringBuffer的區(qū)別。
    【例6-9】
    public class E6_9{
     public static void main(String args[]) {
      String s1="a";
      StringBuffer sb1=new StringBuffer("a");
      
      StringBuffer sb2=sb1;
      String s2="a"+"b";
      sb1.append("b");
        
      System.out.println(s1==s2);
      System.out.println(sb1==sb2);
      
     } 
    }
    上例輸出的是:
    flase
    true
    上例可以證明這樣的結(jié)論:String是不可變長的字符串,對String做任何的修改將生成新的字符串,而StringBuffer是可變長的字符串,不論怎么更動還是同一個字符串。

    posted on 2008-06-16 17:27 飛行魚 閱讀(407) 評論(0)  編輯  收藏 所屬分類: Java基礎(chǔ)

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 久久久久亚洲精品影视| 在线综合亚洲欧洲综合网站 | 国产免费黄色无码视频| 亚洲AV日韩AV永久无码久久 | 亚洲日韩中文在线精品第一| 免费观看男人吊女人视频| 亚洲人成7777| 亚洲毛片αv无线播放一区| 国产桃色在线成免费视频| 一出一进一爽一粗一大视频免费的| 99ri精品国产亚洲| 日韩精品亚洲专区在线观看| 在线免费观看亚洲| 国产成人亚洲精品91专区高清| 亚洲国产精品久久久久| 四虎亚洲国产成人久久精品| 100000免费啪啪18免进| 久久高潮一级毛片免费| 亚洲а∨精品天堂在线| 亚洲国产精品综合久久2007| 久久久久亚洲AV无码专区桃色| 99精品国产免费久久久久久下载 | 黄床大片30分钟免费看| 亚洲成人午夜电影| 黑人精品videos亚洲人| 国产小视频免费观看| 国产在线观看麻豆91精品免费| 韩国免费A级毛片久久| 亚洲AⅤ男人的天堂在线观看| 亚洲成人高清在线观看| 久久精品国产亚洲夜色AV网站| 亚洲成av人片不卡无码久久| 无人影院手机版在线观看免费 | 免费不卡在线观看AV| av电影在线免费看| 国产精品亚洲小说专区| 亚洲伊人久久大香线蕉AV| 亚洲一区无码中文字幕乱码| 亚洲视频在线观看免费| 亚洲国产精品一区二区成人片国内| 亚洲熟伦熟女新五十路熟妇 |