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

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

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

    posts - 6,comments - 2,trackbacks - 0
    轉自:
    http://my.oschina.net/jawava/blog/8574


    發現這個社區不錯,所以也湊個熱鬧。

    第一篇日志,一定要動手寫才有誠意。

    這兩天要給剛做的外網系統登錄頁面加驗證碼,以前沒做過。上網搜了一下,資料很多。
    驗證碼校驗稱作captcha:
    Completely Automated Public Test to tell Computers and Humans Apart
    專業點兒的翻譯是:全自動區分計算機和人類的圖靈測試。
    CAPTCHA的目的很明確,就是區分計算機和人類的一種程序算法,
    這種程序必須能生成并評價人類能很容易通過但計算機卻通不過的測試。

    網上能查到不少實現方案,簡單的寫個jsp就行了,技術含量不高。搜了一圈后,
    感覺還是用個正規點兒比較合適,然后就鎖定了JCaptcha,到其官方網站上看了看:
    http://jcaptcha.octo.com/confluence/display/general/Home

    JCaptcha提供了
    Provide robust and reliable CAPTCHA implementation framework for JAVA
    Provide accessible CAPTCHA implementations
    Provide multi-type challenge (text, sound, image)

    它用上百個類來實現了如此簡單的功能,這是為什么呢?官方給的解釋是
    1、學術界能不斷的發明(或發現)一些人類容易處理而機器不能很好處理的問題。
    JCaptcha高屋建瓴的給出了一種通用的定義和表達這種問題并用于識別的方案。
    也就是識別方案的可擴展性。
    2、實現了若干引擎和組件,通過配置這些引擎和組件,可以方便的修改自己程序
    captcha構件的算法。這樣,在抵御惡意訪問時,可以不用改變代碼,靈活快速的
    改變captcha策略,從而更好的保護系統。

    個人覺的說的挺好,第一點對于我們來說倒是次要的,主要第二點比較有意義。
    然后就試了試。下面是具體需要做的工作:

    一,從官網上下個jcaptcha-1.0-all.jar,加入到項目中,
    官方的2.0還沒有正式版,所以先用1.0吧。

    二,官網上介紹了幾種和項目結合的具體方案,最簡單的方式很快走通,圖片很難看,
    而且不具有可配置性,肯定不行。所以選擇通過spring來整合的方式,
    spring是整合和配置的平臺,把jcaptcha的服務和引擎還有組件配置成spring的bean。
    示例如下:
    <?xml version="1.0" encoding="gb2312"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
            "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans  default-autowire="byName">

        <bean id="captchaServlet" class="com.jawava.XXXX.XXX.TopImageCaptchaServlet" />

    <bean id="captchaService" class="com.octo.captcha.service.multitype.GenericManageableCaptchaService">
    <description>驗證碼服務</description>
    <constructor-arg index="0"><ref bean="imageEngine"/></constructor-arg>
    <constructor-arg index="1"><value>300</value></constructor-arg><!--超時時間 秒-->
    <constructor-arg index="2"><value>20000</value></constructor-arg><!--最大并發數-->
    <constructor-arg index="3"><value>20000</value></constructor-arg>& lt;!--第四個參數官網示例上沒有給出,會報錯,后來看了API才知道少了個參數-->
    </bean>

    <bean id="imageEngine" class="com.octo.captcha.engine.GenericCaptchaEngine">
    <description>圖片引擎</description>
    <constructor-arg index="0">
    <list>
    <ref bean="CaptchaFactory"/>
    </list>
    </constructor-arg>
    </bean>

    <bean id="CaptchaFactory" class="com.octo.captcha.image.gimpy.GimpyFactory" >
    <description>驗證碼工廠</description>
    <constructor-arg><ref bean="wordgen"/></constructor-arg>
    <constructor-arg><ref bean="wordtoimage"/></constructor-arg>
    </bean>

    <bean id="wordgen" class= "com.octo.captcha.component.word.wordgenerator.RandomWordGenerator" >
    <description>文字產生器,提供了好幾種實現,經過比較選用了這種</description>
    <constructor-arg index="0"><value>0123456789</value></constructor-arg>
    </bean>

    <bean id="wordtoimage" class="com.octo.captcha.component.image.wordtoimage.ComposedWordToImage" >
    <description>圖片生成器</description>
    <constructor-arg index="0"><ref bean="fontGenRandom"/></constructor-arg>
    <constructor-arg index="1"><ref bean="backGenUni"/></constructor-arg>
    <constructor-arg index="2"><ref bean="simpleWhitePaster"/></constructor-arg>
    </bean>

    <bean id="fontGenRandom" class="com.octo.captcha.component.image.fontgenerator.RandomFontGenerator" >
    <description>文字轉換圖片</description>
    <constructor-arg index="0"><value>20</value></constructor-arg><!--字體最小尺寸-->
    <constructor-arg index="1"><value>20</value></constructor-arg><!--字體最大尺寸-->
    </bean> 

    <bean id="backGenUni" class="com.octo.captcha.component.image.backgroundgenerator.GradientBackgroundGenerator" >
    <constructor-arg index="0"><value>62</value></constructor-arg><!--背景圖片寬度-->
    <constructor-arg index="1"><value>22</value></constructor-arg><!--背景圖片高度-->
    <constructor-arg type="java.awt.Color" index="2">
    <ref bean="colorGrey"/>
    </constructor-arg> 
    <constructor-arg type="java.awt.Color" index="3">
    <ref bean="colorGreen"/>
    </constructor-arg>

    </bean>

    <bean id="simpleWhitePaster" class="com.octo.captcha.component.image.textpaster.SimpleTextPaster" >
    <constructor-arg type="java.lang.Integer" index="0">
    <value>4</value><!--字符最少個數-->
    </constructor-arg>
    <constructor-arg type="java.lang.Integer" index="1">
    <value>4</value><!--字符最多個數-->
    </constructor-arg>
    <constructor-arg type="java.awt.Color" index="2">
    <ref bean="colorFont"/>
    </constructor-arg>
    </bean>

    <bean id="colorGrey" class="java.awt.Color" >
    <constructor-arg index="0"><value>200</value></constructor-arg>
    <constructor-arg index="1"><value>255</value></constructor-arg>
    <constructor-arg index="2"><value>200</value></constructor-arg>
    </bean>
    <bean id="colorGreen" class="java.awt.Color" >
    <constructor-arg index="0"><value>110</value></constructor-arg>
    <constructor-arg index="1"><value>120</value></constructor-arg>
    <constructor-arg index="2"><value>200</value></constructor-arg>
    </bean>
    <bean id="colorFont" class="java.awt.Color" >
    <constructor-arg index="0"><value>60</value></constructor-arg>
    <constructor-arg index="1"><value>60</value></constructor-arg>
    <constructor-arg index="2"><value>60</value></constructor-arg>
    </bean>
    </beans>



    這里面具體用哪個component,需要看ApI,我把包里提供的現成的組件基本上試了大半,
    最后選擇了如上的配置,選擇的標準一是美觀,二是識別率。識別率太低了,用戶體驗會下降。

    三、配置好后,JCaptcha這邊的工作就完成了。下面就是和項目的結合。
    1、首先專門寫個CaptcahServlet用來獲取驗證碼,由于要在servlet里注入spring的bean,
    所以用了代理的方式。代理類網上有,都是固定寫法,這里就不貼了。
    CaptcahServlet主要部分如下:
    @Override
    public void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
    throws ServletException, IOException, RuntimeException {

    byte[] captchaChallengeAsJpeg = null;
    //輸出jpg的字節流
    ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
    try {
    // get the session id that will identify the generated captcha.
    //the same id must be used to validate the response, the session id is a good candidate!
    String captchaId = httpServletRequest.getSession().getId();
    // call the ImageCaptchaService getChallenge method
    BufferedImage challenge =
    (BufferedImage) captchaService.getChallengeForID(captchaId,
                httpServletRequest.getLocale());

    // a jpeg encoder
        JPEGImageEncoder jpegEncoder =
                JPEGCodec.createJPEGEncoder(jpegOutputStream);
        jpegEncoder.encode(challenge);

    } catch (IllegalArgumentException e) {
        httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    } catch (CaptchaServiceException e) {
        httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        return;
    }

    captchaChallengeAsJpeg = jpegOutputStream.toByteArray();

    // flush it in the response
    httpServletResponse.setHeader("Cache-Control", "no-store");
    httpServletResponse.setHeader("Pragma", "no-cache");
    httpServletResponse.setDateHeader("Expires", 0);
    httpServletResponse.setContentType("image/jpeg");
    ServletOutputStream responseOutputStream =
            httpServletResponse.getOutputStream();
    responseOutputStream.write(captchaChallengeAsJpeg);
    responseOutputStream.flush();
    responseOutputStream.close();
        }



    然后在web.xml配置:

    <servlet>
    <servlet-name>CaptchaProxy</servlet-name>
    <servlet-class>com.jawava.XXXXX.XXX.TopHttpServletProxy</servlet-class>
    <init-param>
    <param-name>targetServlet</param-name>
    <param-value>captchaServlet</param-value>
    </init-param>
    </servlet>
    <servlet-mapping>
    <servlet-name>CaptchaProxy</servlet-name>
    <url-pattern>/topJcaptcha</url-pattern>
    </servlet-mapping>



    2、目前項目的登錄頁面是jsp,結合很方便,在原有頁面上加上
    <img src="/topJcaptcha" /> <input type="text" name="jcaptcha" value="" />
    那個img就是校驗碼。
    需要的話,再加上個換圖片的功能,很簡單。

    3、驗證環節。我是在原來的驗證用戶密碼的邏輯前,加上了驗證碼的校驗邏輯。
    很簡單的幾句話,代碼如下:

    Boolean isResponseCorrect =Boolean.FALSE;
            //需要sessionId 來驗證校驗碼
            String sessionId = request.getSession().getId();
    //首先校驗驗證碼
            try {
                isResponseCorrect = captchaService.validateResponseForID(sessionId,
                 captcha_input);
                if(!isResponseCorrect) {
                 throw new RuntimeException("輸入的驗證碼有誤,請重新輸入");
                }
            } catch (CaptchaServiceException e) {
                 //should not happen, may be thrown if the id is not valid
             throw new RuntimeException("校驗驗證碼時出現不明錯誤",e);
            }



    四、如此就可以測試了。通過反復調整參數,達到了比較美觀的效果,但是文字始終
    不在圖片的正中,而是靠上,甚至都把文字掩蓋了一半。翻看了半天API,也看不出
    個端倪。只好去翻源代碼了。問題出在
    com.octo.captcha.component.image.textpaster.SimpleTextPaster這個類,它把文字
    圖片往背景圖片放時,把文字的位置放在了背景圖片一半的高度上,這就是問題所在。
    修改代碼重新打了個jar包。替換一下,重啟后一切ok。
    posted @ 2012-10-18 10:16 achan2bj 閱讀(1308) | 評論 (2)編輯 收藏

    Vector 還是ArrayList――哪一個更好,為什么?


    要回答這個問題不能一概而論,有時候使用Vector比較好;有時是ArrayList,有時候這兩個都不是 

    最好的選擇。你別指望能夠獲得一個簡單肯定答案,因為這要看你用它們干什么。下面有4個要考慮 

    的因素: 

    l API 

    l 同步處理 

    l 數據增長性 

    l 使用模式 

    下面針對這4個方面進行一一探討 

    API

    在由Ken Arnold等編著的《Java Programming Language》(Addison-Wesley, June 2000)一書中有這 

    樣的描述,Vector類似于ArrayList.。所有從API的角度來看這兩個類非常相[b]似。但他們之間也還 

    是有一些主要的區別的。
    同步性

    Vector是同步的。這個類中的一些方法保證了Vector中的對象是線程安全的。而ArrayList則是異步 

    的,因此ArrayList中的對象并不是線程安全的。因為同步的要求會影響執行的效率,所以如果你不 

    需要線程安全的集合那么使用ArrayList是一個很好的選擇,這樣可以避免由于同步帶來的不必要的 

    性能開銷。 

    數據增長

    從內部實現機制來講ArrayList和Vector都是使用數組(Array)來控制集合中的對象。當你向這兩種類 

    型中增加元素的時候,如果元素的數目超出了內部數組目前的長度它們都需要擴展內部數組的長度, 

    Vector缺省情況下自動增長原來一倍的數組長度,ArrayList是原來的50%,所以最后你獲得的這個集 

    合所占的空間總是比你實際需要的要大。所以如果你要在集合中保存大量的數據那么使用Vector有一 

    些優勢,因為你可以通過設置集合的初始化大小來避免不必要的資源開銷。 

    使用模式

    在ArrayList和Vector中,從一個指定的位置(通過索引)查找數據或是在集合的末尾增加、移除一 

    個元素所花費的時間是一樣的,這個時間我們用O(1)表示。但是,如果在集合的其他位置增加或移除 

    元素那么花費的時間會呈線形增長:O(n-i),其中n代表集合中元素的個數,i代表元素增加或移除元 

    素的索引位置。為什么會這樣呢?以為在進行上述操作的時候集合中第i和第i個元素之后的所有元素 

    都要執行位移的操作。這一切意味著什么呢? 

    這意味著,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或 

    ArrayList都可以。如果是其他操作,你最好選擇其他的集合操作類。比如,LinkList集合類在增加 

    或移除集合中任何位置的元素所花費的時間都是一樣的—O(1),但它在索引一個元素的使用缺比較慢 

    -O(i),其中i是索引的位置.使用ArrayList也很容易,因為你可以簡單的使用索引來代替創建 

    iterator對象的操作。LinkList也會為每個插入的元素創建對象,所有你要明白它也會帶來額外的開 

    銷。 

    最后,在《Practical Java》一書中Peter Haggar建議使用一個簡單的數組(Array)來代替Vector 

    或ArrayList。尤其是對于執行效率要求高的程序更應如此。因為使用數組(Array)避免了同步、額外 

    的方法調用和不必要的重新分配空間的操作。

    posted @ 2012-09-29 09:38 achan2bj| 編輯 收藏
         摘要: JAVA相關基礎知識1、面向對象的特征有哪些方面 1.抽象:抽象就是忽略一個主題中與當前目標無關的那些方面,以便更充分地注意與當前目標有關的方面。抽象并不打算了解全部問題,而只是選擇其中的一部分,暫時不用部分細節。抽象包括兩個方面,一是過程抽象,二是數據抽象。2.繼承:繼承是一種聯結類的層次模型,并且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。對象的一個新類可以從現有的類中派生,這個過程稱...  閱讀全文
    posted @ 2012-09-29 09:31 achan2bj| 編輯 收藏
    1.JAVA多線程實現方式
    JAVA多線程實現方式主要有三種:繼承Thread類、實現Runnable接口、使用ExecutorService、Callable、Future實現有返回結果的多線程。其中前兩種方式線程執行完后都沒有返回值,只有最后一種是帶返回值的。

    2.繼承Thread類實現多線程
    繼承Thread類的方法盡管被我列為一種多線程實現方式,但Thread本質上也是實現了Runnable接口的一個實例,它代表一個線程的實例,并且,啟動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啟動一個新線程,并執行run()方法。這種方式實現多線程很簡單,通過自己的類直接extend Thread,并復寫run()方法,就可以啟動新線程并執行自己定義的run()方法。例如:
    public class MyThread extends Thread {
      public void run() {
       System.out.println("MyThread.run()");
      }
    }
    在合適的地方啟動線程如下:
    MyThread myThread1 = new MyThread();
    MyThread myThread2 = new MyThread();
    myThread1.start();
    myThread2.start();

    3.實現Runnable接口方式實現多線程
    如果自己的類已經extends另一個類,就無法直接extends Thread,此時,必須實現一個Runnable接口,如下:
    public class MyThread extends OtherClass implements Runnable {
      public void run() {
       System.out.println("MyThread.run()");
      }
    }
    為了啟動MyThread,需要首先實例化一個Thread,并傳入自己的MyThread實例:
    MyThread myThread = new MyThread();
    Thread thread = new Thread(myThread);
    thread.start();
    事實上,當傳入一個Runnable target參數給Thread后,Thread的run()方法就會調用target.run(),參考JDK源代碼:
    public void run() {
      if (target != null) {
       target.run();
      }
    }

    4.使用ExecutorService、Callable、Future實現有返回結果的多線程
    ExecutorService、Callable、Future這個對象實際上都是屬于Executor框架中的功能類。想要詳細了解Executor框架的可以訪問http://www.javaeye.com/topic/366591 ,這里面對該框架做了很詳細的解釋。返回結果的線程是在JDK1.5中引入的新特征,確實很實用,有了這種特征我就不需要再為了得到返回值而大費周折了,而且即便實現了也可能漏洞百出。
    可返回值的任務必須實現Callable接口,類似的,無返回值的任務必須Runnable接口。執行Callable任務后,可以獲取一個Future的對象,在該對象上調用get就可以獲取到Callable任務返回的Object了,再結合線程池接口ExecutorService就可以實現傳說中有返回結果的多線程了。下面提供了一個完整的有返回結果的多線程測試例子,在JDK1.5下驗證過沒問題可以直接使用。代碼如下:

    import java.util.concurrent.*;
    import java.util.Date;
    import java.util.List;
    import java.util.ArrayList;

    /**
    * Java線程:有返回值的線程
    *
    * @author wb_qiuquan.ying
    */
    @SuppressWarnings("unchecked")
    public class Test {
    public static void main(String[] args) throws ExecutionException,
        InterruptedException {
       System.out.println("----程序開始運行----");
       Date date1 = new Date();

       int taskSize = 5;
       // 創建一個線程池
       ExecutorService pool = Executors.newFixedThreadPool(taskSize);
       // 創建多個有返回值的任務
       List<Future> list = new ArrayList<Future>();
       for (int i = 0; i < taskSize; i++) {
        Callable c = new MyCallable(i + " ");
        // 執行任務并獲取Future對象
        Future f = pool.submit(c);
        // System.out.println(">>>" + f.get().toString());
        list.add(f);
       }
       // 關閉線程池
       pool.shutdown();

       // 獲取所有并發任務的運行結果
       for (Future f : list) {
        // 從Future對象上獲取任務的返回值,并輸出到控制臺
        System.out.println(">>>" + f.get().toString());
       }

       Date date2 = new Date();
       System.out.println("----程序結束運行----,程序運行時間【"
         + (date2.getTime() - date1.getTime()) + "毫秒】");
    }
    }

    class MyCallable implements Callable<Object> {
    private String taskNum;

    MyCallable(String taskNum) {
       this.taskNum = taskNum;
    }

    public Object call() throws Exception {
       System.out.println(">>>" + taskNum + "任務啟動");
       Date dateTmp1 = new Date();
       Thread.sleep(1000);
       Date dateTmp2 = new Date();
       long time = dateTmp2.getTime() - dateTmp1.getTime();
       System.out.println(">>>" + taskNum + "任務終止");
       return taskNum + "任務返回運行結果,當前任務時間【" + time + "毫秒】";
    }
    }


    代碼說明:
    上述代碼中Executors類,提供了一系列工廠方法用于創先線程池,返回的線程池都實現了ExecutorService接口。
    public static ExecutorService newFixedThreadPool(int nThreads)
    創建固定數目線程的線程池。
    public static ExecutorService newCachedThreadPool()
    創建一個可緩存的線程池,調用execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程并添加到池中。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程。
    public static ExecutorService newSingleThreadExecutor()
    創建一個單線程化的Executor。
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    創建一個支持定時及周期性的任務執行的線程池,多數情況下可用來替代Timer類。

    ExecutoreService提供了submit()方法,傳遞一個Callable,或Runnable,返回Future。如果Executor后臺線程池還沒有完成Callable的計算,這調用返回Future對象的get()方法,會阻塞直到計算完成。

    posted @ 2012-09-24 11:18 achan2bj| 編輯 收藏

    String和StringBuffer的區別,網上資料可以說是數不勝數,但是看到這篇文章,感覺里面做的小例子很有代表性,所以轉一下,并自己做了一點總結。

    在java中有3個類來負責字符的操作。

    1.Character 是進行單個字符操作的,

    2.String 對一串字符進行操作。不可變類。

    3.StringBuffer 也是對一串字符進行操作,但是可變類。

    String:
    是對象不是原始類型.
    為不可變對象,一旦被創建,就不能修改它的值.
    對于已經存在的String對象的修改都是重新創建一個新的對象,然后把新的值保存進去.
    String 是final類,即不能被繼承.

    StringBuffer:
    是一個可變對象,當對他進行修改的時候不會像String那樣重新建立對象
    它只能通過構造函數來建立,
    StringBuffer sb = new StringBuffer();
    note:不能通過付值符號對他進行付值.
    sb = "welcome to here!";//error
    對象被建立以后,在內存中就會分配內存空間,并初始保存一個null.向StringBuffer
    中付值的時候可以通過它的append方法.
    sb.append("hello");

    字符串連接操作中StringBuffer的效率要比String高:

    String str = new String("welcome to ");
    str += "here";
    的處理步驟實際上是通過建立一個StringBuffer,讓侯調用append(),最后
    再將StringBuffer toSting();
    這樣的話String的連接操作就比StringBuffer多出了一些附加操作,當然效率上要打折扣.

    并且由于String 對象是不可變對象,每次操作Sting 都會重新建立新的對象來保存新的值.
    這樣原來的對象就沒用了,就要被垃圾回收.這也是要影響性能的.

    看看以下代碼:
    將26個英文字母重復加了5000次,

    1.         String tempstr = "abcdefghijklmnopqrstuvwxyz";
    2.         int times = 5000;
    3.         long lstart1 = System.currentTimeMillis();
    4.         String str = "";
    5.         for (int i = 0; i < times; i++) {
    6.             str += tempstr;
    7.         }
    8.         long lend1 = System.currentTimeMillis();
    9.         long time = (lend1 - lstart1);
    10.         System.out.println(time);

    可惜我的計算機不是超級計算機,得到的結果每次不一定一樣一般為 46687左右。
    也就是46秒。
    我們再看看以下代碼

    1.         String tempstr = "abcdefghijklmnopqrstuvwxyz";
    2.         int times = 5000;
    3.         long lstart2 = System.currentTimeMillis();
    4.         StringBuffer sb = new StringBuffer();
    5.         for (int i = 0; i < times; i++) {
    6.             sb.append(tempstr);
    7.         }
    8.         long lend2 = System.currentTimeMillis();
    9.         long time2 = (lend2 - lstart2);
    10.         System.out.println(time2);

    得到的結果為 16 有時還是 0
    所以結論很明顯,StringBuffer 的速度幾乎是String 上萬倍。當然這個數據不是很準確。因為循環的次數在100000次的時候,差異更大。不信你試試。

    根據上面所說:

    str += "here";
    的處理步驟實際上是通過建立一個StringBuffer,讓侯調用append(),最后
    再將StringBuffer toSting();

    所以str += "here";可以等同于

    StringBuffer sb = new StringBuffer(str);

    sb.append("here");

    str = sb.toString();

    所以上面直接利用"+"來連接String的代碼可以基本等同于以下代碼

    1.         String tempstr = "abcdefghijklmnopqrstuvwxyz";
    2.         int times = 5000;
    3.         long lstart2 = System.currentTimeMillis();
    4.         String str = "";
    5.         for (int i = 0; i < times; i++) {
    6.             StringBuffer sb = new StringBuffer(str);
    7.             sb.append(tempstr);
    8.             str = sb.toString();
    9.         }
    10.         long lend2 = System.currentTimeMillis();
    11.         long time2 = (lend2 - lstart2);
    12.         System.out.println(time2);

    平均執行時間為46922左右,也就是46秒。

    總結: 如果在程序中需要對字符串進行頻繁的修改連接操作的話.使用StringBuffer性能會更高

    ===========================================================================================


    自從Java 5.0發布以后,我們的比較列表上將多出一個對象了,這就是StringBuilder類。String類是不可變類,任何對String的改變都會引發新的String對象的生成;而StringBuffer則是可變類,任何對它所指代的字符串的改變都不會產生新的對象,可變和不可變類這一對對象已經齊全了,那么為什么還要引入新的StringBuilder類干嗎?相信大家都有此疑問,我也如此。下面,我們就來看看引入該類的原因。

          為什么會出現那么多比較String和StringBuffer的文章?

          原因在于當改變字符串內容時,采用StringBuffer能獲得更好的性能。既然是為了獲得更好的性能,那么采用StringBuffer能夠獲得最好的性能嗎?

          答案是NO!

          為什么?

          如果你讀過《Think in Java》,而且對里面描述HashTable和HashMap區別的那部分章節比較熟悉的話,你一定也明白了原因所在。對,就是支持線程同步保證線程安全而導致性能下降的問題。HashTable是線程安全的,很多方法都是synchronized方法,而HashMap不是線程安全的,但其在單線程程序中的性能比HashTable要高。StringBuffer和StringBuilder類的區別也在于此,新引入的StringBuilder類不是線程安全的,但其在單線程中的性能比StringBuffer高。如果你對此不太相信,可以試試下面的例子:

    package com.hct.test;

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;

    /**
    * @author: chengtai.he
    * @created:2009-12-9 上午09:59:57
    */
    public class StringBuilderTester {
    private static final String base = " base string. ";
    private static final int count = 2000000;

    public static void stringTest() {
      long begin, end;
      begin = System.currentTimeMillis();
      String test = new String(base);
      for (int i = 0; i < count/100; i++) {
       test = test + " add ";
      }
      end = System.currentTimeMillis();
      System.out.println((end - begin)
        + " millis has elapsed when used String. ");
    }

    public static void stringBufferTest() {
      long begin, end;
      begin = System.currentTimeMillis();
      StringBuffer test = new StringBuffer(base);
      for (int i = 0; i < count; i++) {
       test = test.append(" add ");
      }
      end = System.currentTimeMillis();
      System.out.println((end - begin)
        + " millis has elapsed when used StringBuffer. ");
    }

    public static void stringBuilderTest() {
      long begin, end;
      begin = System.currentTimeMillis();
      StringBuilder test = new StringBuilder(base);
      for (int i = 0; i < count; i++) {
       test = test.append(" add ");
      }
      end = System.currentTimeMillis();
      System.out.println((end - begin)
        + " millis has elapsed when used StringBuilder. ");
    }

    public static String appendItemsToStringBuiler(List list) {
      StringBuilder b = new StringBuilder();

      for (Iterator i = list.iterator(); i.hasNext();) {
       b.append(i.next()).append(" ");
      }

      return b.toString();
    }

    public static void addToStringBuilder() {
      List list = new ArrayList();
      list.add(" I ");
      list.add(" play ");
      list.add(" Bourgeois ");
      list.add(" guitars ");
      list.add(" and ");
      list.add(" Huber ");
      list.add(" banjos ");

      System.out.println(StringBuilderTester.appendItemsToStirngBuffer(list));
    }

    public static String appendItemsToStirngBuffer(List list) {
      StringBuffer b = new StringBuffer();

      for (Iterator i = list.iterator(); i.hasNext();) {
       b.append(i.next()).append(" ");
      }

      return b.toString();
    }

    public static void addToStringBuffer() {
      List list = new ArrayList();
      list.add(" I ");
      list.add(" play ");
      list.add(" Bourgeois ");
      list.add(" guitars ");
      list.add(" and ");
      list.add(" Huber ");
      list.add(" banjos ");

      System.out.println(StringBuilderTester.appendItemsToStirngBuffer(list));
    }

    public static void main(String[] args) {
      stringTest();
      stringBufferTest();
      stringBuilderTest();
      addToStringBuffer();
      addToStringBuilder();
    }
    }

    上面的程序結果如下:
    5266 millis has elapsed when used String.
    375 millis has elapsed when used StringBuffer.
    281 millis has elapsed when used StringBuilder.
    I   play   Bourgeois   guitars   and   Huber   banjos 
    I   play   Bourgeois   guitars   and   Huber   banjos
    從上面的結果來看,這三個類在單線程程序中的性能差別一目了然,采用String對象時,即使運行次數僅是采用其他對象的1/100,其執行時間仍然比其他對象高出25倍以上;而采用StringBuffer對象和采用StringBuilder對象的差別也比較明顯,前者是后者的1.5倍左右。由此可見,如果我們的程序是在單線程下運行,或者是不必考慮到線程同步問題,我們應該優先使用StringBuilder類;當然,如果要保證線程安全,自然非StringBuffer莫屬了。

    除了對多線程的支持不一樣外,這兩個類的使用幾乎沒有任何差別,上面的例子就是個很好的說明。appendItemsToStringBuiler和appendItemsToStirngBuffer兩個方法除了采用的對象分別為StringBuilder和StringBuffer外,其他完全相同,而效果也完全相同。


    posted @ 2012-09-24 09:44 achan2bj| 編輯 收藏
    新開通BlogJava,收到注冊成功郵件其實不是今天,也是最近的事了。在這里記錄與java有關或無關的事情,讓自己心情增加多一個空間。
    posted @ 2012-09-24 08:42 achan2bj 閱讀(95) | 評論 (0)編輯 收藏
    主站蜘蛛池模板: 国产午夜亚洲精品午夜鲁丝片 | 亚洲人成人网毛片在线播放| 日韩av无码免费播放| 亚洲美女高清一区二区三区 | 青青操免费在线视频| 久久亚洲欧洲国产综合| 久久免费国产精品| 久久亚洲国产欧洲精品一| 久久国产免费观看精品| 久久久国产精品亚洲一区| 久久免费看少妇高潮V片特黄| 久久久久亚洲av无码尤物| 蜜桃视频在线观看免费视频网站WWW | ASS亚洲熟妇毛茸茸PICS| 国产精品视频永久免费播放| 亚洲欧美日韩综合久久久| 国产禁女女网站免费看| 黄床大片30分钟免费看| 亚洲国产小视频精品久久久三级| 久久成人18免费网站| 久久精品国产亚洲av麻豆| 在线观看永久免费| 亚洲国产精品无码观看久久| 又粗又大又长又爽免费视频| a高清免费毛片久久| 久久久综合亚洲色一区二区三区 | 亚洲日韩中文字幕一区| 国产精品成人四虎免费视频| 精品一区二区三区高清免费观看| 久久九九亚洲精品| 成年男女免费视频网站| 阿v视频免费在线观看| 亚洲精品成人无码中文毛片不卡| 95老司机免费福利| 看Aⅴ免费毛片手机播放| 亚洲av无码一区二区三区网站| 成人免费毛片内射美女APP| 免费人成又黄又爽的视频在线电影| 国产精品亚洲аv无码播放| 少妇高潮太爽了在线观看免费| 国产精品亚洲一区二区三区 |