﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-原谅我这一生不羁放纵爱自由</title><link>http://www.blogjava.net/achan2bj/</link><description /><language>zh-cn</language><lastBuildDate>Sun, 12 Apr 2026 09:41:23 GMT</lastBuildDate><pubDate>Sun, 12 Apr 2026 09:41:23 GMT</pubDate><ttl>60</ttl><item><title>运用Jcaptcha做验证码总结 </title><link>http://www.blogjava.net/achan2bj/archive/2012/10/18/389790.html</link><dc:creator>achan2bj</dc:creator><author>achan2bj</author><pubDate>Thu, 18 Oct 2012 02:16:00 GMT</pubDate><guid>http://www.blogjava.net/achan2bj/archive/2012/10/18/389790.html</guid><wfw:comment>http://www.blogjava.net/achan2bj/comments/389790.html</wfw:comment><comments>http://www.blogjava.net/achan2bj/archive/2012/10/18/389790.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/achan2bj/comments/commentRss/389790.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/achan2bj/services/trackbacks/389790.html</trackback:ping><description><![CDATA[转自：<div>http://my.oschina.net/jawava/blog/8574 </div><br /><br /><div><p>发现这个社区不错，所以也凑个热闹。</p> <p>第一篇日志，一定要动手写才有诚意。</p>  这两天要给刚做的外网系统登录页面加验证码，以前没做过。上网搜了一下，资料很多。<br style="padding: 0px; margin: 0px;" />验证码校验称作captcha：<br style="padding: 0px; margin: 0px;" />Completely Automated Public Test to tell Computers and Humans Apart<br style="padding: 0px; margin: 0px;" />专业点儿的翻译是：全自动区分计算机和人类的图灵测试。<br style="padding: 0px; margin: 0px;" />CAPTCHA的目的很明确，就是区分计算机和人类的一种程序算法，<br style="padding: 0px; margin: 0px;" />这种程序必须能生成并评价人类能很容易通过但计算机却通不过的测试。<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />网上能查到不少实现方案，简单的写个jsp就行了，技术含量不高。搜了一圈后，<br style="padding: 0px; margin: 0px;" />感觉还是用个正规点儿比较合适，然后就锁定了JCaptcha，到其官方网站上看了看：<br style="padding: 0px; margin: 0px;" /><span style="padding: 0px; margin: 0px; color: #000080;">http://jcaptcha.octo.com/confluence/display/general/Home</span><br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />JCaptcha提供了<br style="padding: 0px; margin: 0px;" />Provide robust and reliable CAPTCHA implementation framework for JAVA<br style="padding: 0px; margin: 0px;" />Provide accessible CAPTCHA implementations<br style="padding: 0px; margin: 0px;" />Provide multi-type challenge (text, sound, image)<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />它用上百个类来实现了如此简单的功能，这是为什么呢？官方给的解释是<br style="padding: 0px; margin: 0px;" />1、学术界能不断的发明（或发现）一些人类容易处理而机器不能很好处理的问题。<br style="padding: 0px; margin: 0px;" />JCaptcha高屋建瓴的给出了一种通用的定义和表达这种问题并用于识别的方案。<br style="padding: 0px; margin: 0px;" />也就是识别方案的可扩展性。<br style="padding: 0px; margin: 0px;" />2、实现了若干引擎和组件，通过配置这些引擎和组件，可以方便的修改自己程序<br style="padding: 0px; margin: 0px;" />captcha构件的算法。这样，在抵御恶意访问时，可以不用改变代码，灵活快速的<br style="padding: 0px; margin: 0px;" />改变captcha策略，从而更好的保护系统。<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />个人觉的说的挺好，第一点对于我们来说倒是次要的，主要第二点比较有意义。<br style="padding: 0px; margin: 0px;" />然后就试了试。下面是具体需要做的工作：<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />一，从官网上下个jcaptcha-1.0-all.jar，加入到项目中，<br style="padding: 0px; margin: 0px;" />官方的2.0还没有正式版，所以先用1.0吧。<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />二，官网上介绍了几种和项目结合的具体方案，最简单的方式很快走通，图片很难看，<br style="padding: 0px; margin: 0px;" />而且不具有可配置性，肯定不行。所以选择通过spring来整合的方式，<br style="padding: 0px; margin: 0px;" />spring是整合和配置的平台，把jcaptcha的服务和引擎还有组件配置成spring的bean。<br style="padding: 0px; margin: 0px;" />示例如下：<br style="padding: 0px; margin: 0px;" /><table style="padding: 0px; margin: 0px;"><tbody><tr style="padding: 0px; margin: 0px;"> <td style="padding: 0px; margin: 0px;">&lt;?xml version="1.0" encoding="gb2312"?&gt;<br style="padding: 0px; margin: 0px;" />&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"http://www.springframework.org/dtd/spring-beans.dtd"&gt;<br style="padding: 0px; margin: 0px;" />&lt;beans&nbsp;&nbsp;default-autowire="byName"&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="captchaServlet" class="com.jawava.XXXX.XXX.TopImageCaptchaServlet" /&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="captchaService" class="com.octo.captcha.service.multitype.GenericManageableCaptchaService"&gt;<br style="padding: 0px; margin: 0px;" />&lt;description&gt;验证码服务&lt;/description&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;&lt;ref bean="imageEngine"/&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="1"&gt;&lt;value&gt;300&lt;/value&gt;&lt;/constructor-arg&gt;&lt;!--超时时间 秒--&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="2"&gt;&lt;value&gt;20000&lt;/value&gt;&lt;/constructor-arg&gt;&lt;!--最大并发数--&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg   index="3"&gt;&lt;value&gt;20000&lt;/value&gt;&lt;/constructor-arg&gt;&amp; lt;!--第四个参数官网示例上没有给出，会报错，后来看了API才知道少了个参数--&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="imageEngine" class="com.octo.captcha.engine.GenericCaptchaEngine"&gt;<br style="padding: 0px; margin: 0px;" />&lt;description&gt;图片引擎&lt;/description&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;<br style="padding: 0px; margin: 0px;" />&lt;list&gt;<br style="padding: 0px; margin: 0px;" />&lt;ref bean="CaptchaFactory"/&gt;<br style="padding: 0px; margin: 0px;" />&lt;/list&gt;<br style="padding: 0px; margin: 0px;" />&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="CaptchaFactory" class="com.octo.captcha.image.gimpy.GimpyFactory" &gt;<br style="padding: 0px; margin: 0px;" />&lt;description&gt;验证码工厂&lt;/description&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg&gt;&lt;ref bean="wordgen"/&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg&gt;&lt;ref bean="wordtoimage"/&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="wordgen" class= "com.octo.captcha.component.word.wordgenerator.RandomWordGenerator" &gt;<br style="padding: 0px; margin: 0px;" />&lt;description&gt;文字产生器，提供了好几种实现，经过比较选用了这种&lt;/description&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;&lt;value&gt;0123456789&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="wordtoimage" class="com.octo.captcha.component.image.wordtoimage.ComposedWordToImage" &gt;<br style="padding: 0px; margin: 0px;" />&lt;description&gt;图片生成器&lt;/description&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;&lt;ref bean="fontGenRandom"/&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="1"&gt;&lt;ref bean="backGenUni"/&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="2"&gt;&lt;ref bean="simpleWhitePaster"/&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="fontGenRandom" class="com.octo.captcha.component.image.fontgenerator.RandomFontGenerator" &gt;<br style="padding: 0px; margin: 0px;" />&lt;description&gt;文字转换图片&lt;/description&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;&lt;value&gt;20&lt;/value&gt;&lt;/constructor-arg&gt;&lt;!--字体最小尺寸--&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="1"&gt;&lt;value&gt;20&lt;/value&gt;&lt;/constructor-arg&gt;&lt;!--字体最大尺寸--&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;&nbsp;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="backGenUni" class="com.octo.captcha.component.image.backgroundgenerator.GradientBackgroundGenerator" &gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;&lt;value&gt;62&lt;/value&gt;&lt;/constructor-arg&gt;&lt;!--背景图片宽度--&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="1"&gt;&lt;value&gt;22&lt;/value&gt;&lt;/constructor-arg&gt;&lt;!--背景图片高度--&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg type="java.awt.Color" index="2"&gt;<br style="padding: 0px; margin: 0px;" />&lt;ref bean="colorGrey"/&gt;<br style="padding: 0px; margin: 0px;" />&lt;/constructor-arg&gt;&nbsp;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg type="java.awt.Color" index="3"&gt;<br style="padding: 0px; margin: 0px;" />&lt;ref bean="colorGreen"/&gt;<br style="padding: 0px; margin: 0px;" />&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="simpleWhitePaster" class="com.octo.captcha.component.image.textpaster.SimpleTextPaster" &gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg type="java.lang.Integer" index="0"&gt;<br style="padding: 0px; margin: 0px;" />&lt;value&gt;4&lt;/value&gt;&lt;!--字符最少个数--&gt;<br style="padding: 0px; margin: 0px;" />&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg type="java.lang.Integer" index="1"&gt;<br style="padding: 0px; margin: 0px;" />&lt;value&gt;4&lt;/value&gt;&lt;!--字符最多个数--&gt;<br style="padding: 0px; margin: 0px;" />&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg type="java.awt.Color" index="2"&gt;<br style="padding: 0px; margin: 0px;" />&lt;ref bean="colorFont"/&gt;<br style="padding: 0px; margin: 0px;" />&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />&lt;bean id="colorGrey" class="java.awt.Color" &gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;&lt;value&gt;200&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="1"&gt;&lt;value&gt;255&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="2"&gt;&lt;value&gt;200&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" />&lt;bean id="colorGreen" class="java.awt.Color" &gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;&lt;value&gt;110&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="1"&gt;&lt;value&gt;120&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="2"&gt;&lt;value&gt;200&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" />&lt;bean id="colorFont" class="java.awt.Color" &gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="0"&gt;&lt;value&gt;60&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="1"&gt;&lt;value&gt;60&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;constructor-arg index="2"&gt;&lt;value&gt;60&lt;/value&gt;&lt;/constructor-arg&gt;<br style="padding: 0px; margin: 0px;" />&lt;/bean&gt;<br style="padding: 0px; margin: 0px;" />&lt;/beans&gt;</td> </tr></tbody></table> <br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />这里面具体用哪个component,需要看ApI，我把包里提供的现成的组件基本上试了大半，<br style="padding: 0px; margin: 0px;" />最后选择了如上的配置，选择的标准一是美观，二是识别率。识别率太低了，用户体验会下降。<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />三、配置好后，JCaptcha这边的工作就完成了。下面就是和项目的结合。<br style="padding: 0px; margin: 0px;" />1、首先专门写个CaptcahServlet用来获取验证码，由于要在servlet里注入spring的bean，<br style="padding: 0px; margin: 0px;" />所以用了代理的方式。代理类网上有，都是固定写法，这里就不贴了。<br style="padding: 0px; margin: 0px;" />CaptcahServlet主要部分如下：<br style="padding: 0px; margin: 0px;" /><table style="padding: 0px; margin: 0px;"><tbody><tr style="padding: 0px; margin: 0px;"> <td style="padding: 0px; margin: 0px;">@Override<br style="padding: 0px; margin: 0px;" />public void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)<br style="padding: 0px; margin: 0px;" />throws ServletException, IOException, RuntimeException {<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />byte[] captchaChallengeAsJpeg = null;<br style="padding: 0px; margin: 0px;" />//输出jpg的字节流<br style="padding: 0px; margin: 0px;" />ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();<br style="padding: 0px; margin: 0px;" />try {<br style="padding: 0px; margin: 0px;" />// get the session id that will identify the generated captcha.<br style="padding: 0px; margin: 0px;" />//the same id must be used to validate the response, the session id is a good candidate!<br style="padding: 0px; margin: 0px;" />String captchaId = httpServletRequest.getSession().getId();<br style="padding: 0px; margin: 0px;" />// call the ImageCaptchaService getChallenge method<br style="padding: 0px; margin: 0px;" />BufferedImage challenge =<br style="padding: 0px; margin: 0px;" />(BufferedImage) captchaService.getChallengeForID(captchaId,<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;httpServletRequest.getLocale());<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />// a jpeg encoder<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;JPEGImageEncoder jpegEncoder =<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JPEGCodec.createJPEGEncoder(jpegOutputStream);<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;jpegEncoder.encode(challenge);<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />} catch (IllegalArgumentException e) {<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;return;<br style="padding: 0px; margin: 0px;" />} catch (CaptchaServiceException e) {<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;return;<br style="padding: 0px; margin: 0px;" />}<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />captchaChallengeAsJpeg = jpegOutputStream.toByteArray();<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />// flush it in the response<br style="padding: 0px; margin: 0px;" />httpServletResponse.setHeader("Cache-Control", "no-store");<br style="padding: 0px; margin: 0px;" />httpServletResponse.setHeader("Pragma", "no-cache");<br style="padding: 0px; margin: 0px;" />httpServletResponse.setDateHeader("Expires", 0);<br style="padding: 0px; margin: 0px;" />httpServletResponse.setContentType("image/jpeg");<br style="padding: 0px; margin: 0px;" />ServletOutputStream responseOutputStream =<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;httpServletResponse.getOutputStream();<br style="padding: 0px; margin: 0px;" />responseOutputStream.write(captchaChallengeAsJpeg);<br style="padding: 0px; margin: 0px;" />responseOutputStream.flush();<br style="padding: 0px; margin: 0px;" />responseOutputStream.close();<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;}</td> </tr></tbody></table> <br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />然后在web.xml配置：<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" /><table style="padding: 0px; margin: 0px;"><tbody><tr style="padding: 0px; margin: 0px;"> <td style="padding: 0px; margin: 0px;">&lt;servlet&gt;<br style="padding: 0px; margin: 0px;" />&lt;servlet-name&gt;CaptchaProxy&lt;/servlet-name&gt;<br style="padding: 0px; margin: 0px;" />&lt;servlet-class&gt;com.jawava.XXXXX.XXX.TopHttpServletProxy&lt;/servlet-class&gt;<br style="padding: 0px; margin: 0px;" />&lt;init-param&gt;<br style="padding: 0px; margin: 0px;" />&lt;param-name&gt;targetServlet&lt;/param-name&gt;<br style="padding: 0px; margin: 0px;" />&lt;param-value&gt;captchaServlet&lt;/param-value&gt;<br style="padding: 0px; margin: 0px;" />&lt;/init-param&gt;<br style="padding: 0px; margin: 0px;" />&lt;/servlet&gt;<br style="padding: 0px; margin: 0px;" />&lt;servlet-mapping&gt;<br style="padding: 0px; margin: 0px;" />&lt;servlet-name&gt;CaptchaProxy&lt;/servlet-name&gt;<br style="padding: 0px; margin: 0px;" />&lt;url-pattern&gt;/topJcaptcha&lt;/url-pattern&gt;<br style="padding: 0px; margin: 0px;" />&lt;/servlet-mapping&gt;</td> </tr></tbody></table> <br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />2、目前项目的登录页面是jsp，结合很方便，在原有页面上加上<br style="padding: 0px; margin: 0px;" />&lt;img src="/topJcaptcha" /&gt; &lt;input type="text" name="jcaptcha" value="" /&gt;<br style="padding: 0px; margin: 0px;" />那个img就是校验码。<br style="padding: 0px; margin: 0px;" />需要的话，再加上个换图片的功能，很简单。<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />3、验证环节。我是在原来的验证用户密码的逻辑前，加上了验证码的校验逻辑。<br style="padding: 0px; margin: 0px;" />很简单的几句话，代码如下：<br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" /><table style="padding: 0px; margin: 0px;"><tbody><tr style="padding: 0px; margin: 0px;"> <td style="padding: 0px; margin: 0px;">Boolean isResponseCorrect =Boolean.FALSE;<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//需要sessionId 来验证校验码<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String sessionId = request.getSession().getId();<br style="padding: 0px; margin: 0px;" />//首先校验验证码<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isResponseCorrect = captchaService.validateResponseForID(sessionId,<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; captcha_input);<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!isResponseCorrect) {<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new RuntimeException("输入的验证码有误，请重新输入");<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (CaptchaServiceException e) {<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //should not happen, may be thrown if the id is not valid<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new RuntimeException("校验验证码时出现不明错误",e);<br style="padding: 0px; margin: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</td> </tr></tbody></table> <br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" /><br style="padding: 0px; margin: 0px;" />四、如此就可以测试了。通过反复调整参数，达到了比较美观的效果，但是文字始终<br style="padding: 0px; margin: 0px;" />不在图片的正中，而是靠上，甚至都把文字掩盖了一半。翻看了半天API，也看不出<br style="padding: 0px; margin: 0px;" />个端倪。只好去翻源代码了。问题出在<br style="padding: 0px; margin: 0px;" />com.octo.captcha.component.image.textpaster.SimpleTextPaster这个类，它把文字<br style="padding: 0px; margin: 0px;" />图片往背景图片放时，把文字的位置放在了背景图片一半的高度上，这就是问题所在。<br style="padding: 0px; margin: 0px;" />修改代码重新打了个jar包。替换一下，重启后一切ok。</div><img src ="http://www.blogjava.net/achan2bj/aggbug/389790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/achan2bj/" target="_blank">achan2bj</a> 2012-10-18 10:16 <a href="http://www.blogjava.net/achan2bj/archive/2012/10/18/389790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ArrayList和Vector的比较【转】</title><link>http://www.blogjava.net/achan2bj/archive/2012/09/29/388819.html</link><dc:creator>achan2bj</dc:creator><author>achan2bj</author><pubDate>Sat, 29 Sep 2012 01:38:00 GMT</pubDate><guid>http://www.blogjava.net/achan2bj/archive/2012/09/29/388819.html</guid><description><![CDATA[<p><strong>Vector 还是ArrayList&#8213;&#8213;哪一个更好，为什么？</strong></p><p><strong><br /></strong>要回答这个问题不能一概而论，有时候使用Vector比较好；有时是ArrayList，有时候这两个都不是&nbsp; </p><p>最好的选择。你别指望能够获得一个简单肯定答案，因为这要看你用它们干什么。下面有4个要考虑&nbsp; </p><p>的因素：&nbsp; </p><p>l API&nbsp; </p><p>l 同步处理&nbsp; </p><p>l 数据增长性&nbsp; </p><p>l 使用模式&nbsp; </p><p>下面针对这4个方面进行一一探讨&nbsp; </p><p><strong>API </strong> </p><p>在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这&nbsp; </p><p>样的描述，Vector类似于ArrayList.。所有从API的角度来看这两个类非常相[b]似。但他们之间也还&nbsp; </p><p>是有一些主要的区别的。<br /><strong>同步性</strong></p><p>Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步&nbsp; </p><p>的，因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率，所以如果你不&nbsp; </p><p>需要线程安全的集合那么使用ArrayList是一个很好的选择，这样可以避免由于同步带来的不必要的&nbsp; </p><p>性能开销。&nbsp; </p><p><strong>数据增长</strong></p><p>从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类&nbsp; </p><p>型中增加元素的时候，如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度，&nbsp; </p><p>Vector缺省情况下自动增长原来一倍的数组长度，ArrayList是原来的50%,所以最后你获得的这个集&nbsp; </p><p>合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一&nbsp; </p><p>些优势，因为你可以通过设置集合的初始化大小来避免不必要的资源开销。&nbsp; </p><p><strong>使用模式</strong></p><p>在ArrayList和Vector中，从一个指定的位置（通过索引）查找数据或是在集合的末尾增加、移除一&nbsp; </p><p>个元素所花费的时间是一样的，这个时间我们用O(1)表示。但是，如果在集合的其他位置增加或移除&nbsp; </p><p>元素那么花费的时间会呈线形增长：O(n-i)，其中n代表集合中元素的个数，i代表元素增加或移除元&nbsp; </p><p>素的索引位置。为什么会这样呢？以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素&nbsp; </p><p>都要执行位移的操作。这一切意味着什么呢？&nbsp; </p><p>这意味着，你只是查找特定位置的元素或只在集合的末端增加、移除元素，那么使用Vector或&nbsp; </p><p>ArrayList都可以。如果是其他操作，你最好选择其他的集合操作类。比如，LinkList集合类在增加&nbsp; </p><p>或移除集合中任何位置的元素所花费的时间都是一样的&#8212;O(1)，但它在索引一个元素的使用缺比较慢&nbsp; </p><p>－O(i),其中i是索引的位置.使用ArrayList也很容易，因为你可以简单的使用索引来代替创建&nbsp; </p><p>iterator对象的操作。LinkList也会为每个插入的元素创建对象，所有你要明白它也会带来额外的开&nbsp; </p><p>销。&nbsp; </p><p>最后，在《Practical Java》一书中Peter Haggar建议使用一个简单的数组（Array）来代替Vector&nbsp; </p><p>或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外&nbsp; </p><p>的方法调用和不必要的重新分配空间的操作。</p><img src ="http://www.blogjava.net/achan2bj/aggbug/388819.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/achan2bj/" target="_blank">achan2bj</a> 2012-09-29 09:38 <a href="http://www.blogjava.net/achan2bj/archive/2012/09/29/388819.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java面试题及答案（基础题122道，代码题19道） [转]</title><link>http://www.blogjava.net/achan2bj/archive/2012/09/29/388818.html</link><dc:creator>achan2bj</dc:creator><author>achan2bj</author><pubDate>Sat, 29 Sep 2012 01:31:00 GMT</pubDate><guid>http://www.blogjava.net/achan2bj/archive/2012/09/29/388818.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: JAVA相关基础知识1、面向对象的特征有哪些方面 1.抽象：抽象就是忽略一个主题中与当前目标无关的那些方面，以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题，而只是选择其中的一部分，暂时不用部分细节。抽象包括两个方面，一是过程抽象，二是数据抽象。2.继承：继承是一种联结类的层次模型，并且允许和鼓励类的重用，它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生，这个过程称...&nbsp;&nbsp;<a href='http://www.blogjava.net/achan2bj/archive/2012/09/29/388818.html'>阅读全文</a><img src ="http://www.blogjava.net/achan2bj/aggbug/388818.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/achan2bj/" target="_blank">achan2bj</a> 2012-09-29 09:31 <a href="http://www.blogjava.net/achan2bj/archive/2012/09/29/388818.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA的多线程实现</title><link>http://www.blogjava.net/achan2bj/archive/2012/09/24/388422.html</link><dc:creator>achan2bj</dc:creator><author>achan2bj</author><pubDate>Mon, 24 Sep 2012 03:18:00 GMT</pubDate><guid>http://www.blogjava.net/achan2bj/archive/2012/09/24/388422.html</guid><description><![CDATA[<strong>1.JAVA多线程实现方式<br /></strong>JAVA多线程实现方式主要有三种：继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值，只有最后一种是带返回值的。<p><strong>2.继承Thread类实现多线程<br /></strong>继承Thread类的方法尽管被我列为一种多线程实现方式，但Thread本质上也是实现了Runnable接口的一个实例，它代表一个线程的实例，并且，启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法，它将启动一个新线程，并执行run()方法。这种方式实现多线程很简单，通过自己的类直接extend Thread，并复写run()方法，就可以启动新线程并执行自己定义的run()方法。例如：<br />public class MyThread extends Thread {<br />　　public void run() {<br />　　 System.out.println("MyThread.run()");<br />　　}<br />}<br />在合适的地方启动线程如下：<br />MyThread myThread1 = new MyThread();<br />MyThread myThread2 = new MyThread();<br />myThread1.start();<br />myThread2.start();</p><p><strong>3.实现Runnable接口方式实现多线程<br /></strong>如果自己的类已经extends另一个类，就无法直接extends Thread，此时，必须实现一个Runnable接口，如下：<br />public class MyThread extends OtherClass implements Runnable {<br />　　public void run() {<br />　　 System.out.println("MyThread.run()");<br />　　}<br />}<br />为了启动MyThread，需要首先实例化一个Thread，并传入自己的MyThread实例：<br />MyThread myThread = new MyThread();<br />Thread thread = new Thread(myThread);<br />thread.start();<br />事实上，当传入一个Runnable target参数给Thread后，Thread的run()方法就会调用target.run()，参考JDK源代码：<br />public void run() {<br />　　if (target != null) {<br />　　 target.run();<br />　　}<br />}</p><p><strong>4.使用ExecutorService、Callable、Future实现有返回结果的多线程</strong><br />ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类。想要详细了解Executor框架的可以访问<a href="http://www.javaeye.com/topic/366591">http://www.javaeye.com/topic/366591</a> ，这里面对该框架做了很详细的解释。返回结果的线程是在JDK1.5中引入的新特征，确实很实用，有了这种特征我就不需要再为了得到返回值而大费周折了，而且即便实现了也可能漏洞百出。<br />可返回值的任务必须实现Callable接口，类似的，无返回值的任务必须Runnable接口。执行Callable任务后，可以获取一个Future的对象，在该对象上调用get就可以获取到Callable任务返回的Object了，再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。下面提供了一个完整的有返回结果的多线程测试例子，在JDK1.5下验证过没问题可以直接使用。代码如下：</p><p>import java.util.concurrent.*;<br />import java.util.Date;<br />import java.util.List;<br />import java.util.ArrayList;</p><p>/**<br />* Java线程：有返回值的线程<br />* <br />* @author wb_qiuquan.ying<br />*/<br />@SuppressWarnings("unchecked")<br />public class Test {<br />public static void main(String[] args) throws ExecutionException,<br />&nbsp;&nbsp;&nbsp; InterruptedException {<br />&nbsp;&nbsp; System.out.println("----程序开始运行----");<br />&nbsp;&nbsp; Date date1 = new Date();</p><p>&nbsp;&nbsp; int taskSize = 5;<br />&nbsp;&nbsp; // 创建一个线程池<br />&nbsp;&nbsp; ExecutorService pool = Executors.newFixedThreadPool(taskSize);<br />&nbsp;&nbsp; // 创建多个有返回值的任务<br />&nbsp;&nbsp; List&lt;Future&gt; list = new ArrayList&lt;Future&gt;();<br />&nbsp;&nbsp; for (int i = 0; i &lt; taskSize; i++) {<br />&nbsp;&nbsp;&nbsp; Callable c = new MyCallable(i + " ");<br />&nbsp;&nbsp;&nbsp; // 执行任务并获取Future对象<br />&nbsp;&nbsp;&nbsp; Future f = pool.submit(c);<br />&nbsp;&nbsp;&nbsp; // System.out.println("&gt;&gt;&gt;" + f.get().toString());<br />&nbsp;&nbsp;&nbsp; list.add(f);<br />&nbsp;&nbsp; }<br />&nbsp;&nbsp; // 关闭线程池<br />&nbsp;&nbsp; pool.shutdown();</p><p>&nbsp;&nbsp; // 获取所有并发任务的运行结果<br />&nbsp;&nbsp; for (Future f : list) {<br />&nbsp;&nbsp;&nbsp; // 从Future对象上获取任务的返回值，并输出到控制台<br />&nbsp;&nbsp;&nbsp; System.out.println("&gt;&gt;&gt;" + f.get().toString());<br />&nbsp;&nbsp; }</p><p>&nbsp;&nbsp; Date date2 = new Date();<br />&nbsp;&nbsp; System.out.println("----程序结束运行----，程序运行时间【"<br />&nbsp;&nbsp;&nbsp;&nbsp; + (date2.getTime() - date1.getTime()) + "毫秒】");<br />}<br />}</p><p>class MyCallable implements Callable&lt;Object&gt; {<br />private String taskNum;</p><p>MyCallable(String taskNum) {<br />&nbsp;&nbsp; this.taskNum = taskNum;<br />}</p><p>public Object call() throws Exception {<br />&nbsp;&nbsp; System.out.println("&gt;&gt;&gt;" + taskNum + "任务启动");<br />&nbsp;&nbsp; Date dateTmp1 = new Date();<br />&nbsp;&nbsp; Thread.sleep(1000);<br />&nbsp;&nbsp; Date dateTmp2 = new Date();<br />&nbsp;&nbsp; long time = dateTmp2.getTime() - dateTmp1.getTime();<br />&nbsp;&nbsp; System.out.println("&gt;&gt;&gt;" + taskNum + "任务终止");<br />&nbsp;&nbsp; return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";<br />}<br />}</p><p><br />代码说明：<br />上述代码中Executors类，提供了一系列工厂方法用于创先线程池，返回的线程池都实现了ExecutorService接口。<br />public static ExecutorService newFixedThreadPool(int nThreads) <br />创建固定数目线程的线程池。<br />public static ExecutorService newCachedThreadPool() <br />创建一个可缓存的线程池，调用execute 将重用以前构造的线程（如果线程可用）。如果现有线程没有可用的，则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。<br />public static ExecutorService newSingleThreadExecutor() <br />创建一个单线程化的Executor。<br />public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) <br />创建一个支持定时及周期性的任务执行的线程池，多数情况下可用来替代Timer类。</p><p>ExecutoreService提供了submit()方法，传递一个Callable，或Runnable，返回Future。如果Executor后台线程池还没有完成Callable的计算，这调用返回Future对象的get()方法，会阻塞直到计算完成。</p><img src ="http://www.blogjava.net/achan2bj/aggbug/388422.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/achan2bj/" target="_blank">achan2bj</a> 2012-09-24 11:18 <a href="http://www.blogjava.net/achan2bj/archive/2012/09/24/388422.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>String,StringBuffer及StringBuilder</title><link>http://www.blogjava.net/achan2bj/archive/2012/09/24/388410.html</link><dc:creator>achan2bj</dc:creator><author>achan2bj</author><pubDate>Mon, 24 Sep 2012 01:44:00 GMT</pubDate><guid>http://www.blogjava.net/achan2bj/archive/2012/09/24/388410.html</guid><description><![CDATA[
<p>String和StringBuffer的区别，网上资料可以说是数不胜数，但是看到这篇文章，感觉里面做的小例子很有代表性，所以转一下，并自己做了一点总结。</p><p> </p><p>在java中有3个类来负责字符的操作。 </p><p>1.Character 是进行单个字符操作的，</p><p>2.String 对一串字符进行操作。不可变类。</p><p>3.StringBuffer 也是对一串字符进行操作，但是可变类。</p><p><span style="color: red;">String:</span><br /><span style="color: red;">是对象不是原始类型.</span><br /><span style="color: red;">为不可变对象,一旦被创建,就不能修改它的值.</span><br /><span style="color: red;">对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.</span><br /><span style="color: red;">String 是final类,即不能被继承.</span></p><p><span style="color: red;">StringBuffer:</span><br /><span style="color: red;">是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象</span><br /><span style="color: red;">它只能通过构造函数来建立,</span><br />StringBuffer sb = new StringBuffer();<br />note:不能通过付值符号对他进行付值. <br />sb = "welcome to here!";//error<br />对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer<br />中付值的时候可以通过它的append方法.<br />sb.append("hello");</p><p>字符串连接操作中StringBuffer的效率要比String高:</p><p>String str = new String("welcome to ");<br />str += "here";<br />的处理步骤实际上是通过建立一个StringBuffer,让侯调用append(),最后<br />再将StringBuffer toSting();<br />这样的话String的连接操作就比StringBuffer多出了一些附加操作,当然效率上要打折扣.</p><p>并且由于String 对象是不可变对象,每次操作Sting 都会重新建立新的对象来保存新的值.<br />这样原来的对象就没用了,就要被垃圾回收.这也是要影响性能的. </p><p>看看以下代码：<br />将26个英文字母重复加了5000次，</p><div class="highlighter"><ol class="highlighter-j"><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String tempstr = </span><span class="string">"abcdefghijklmnopqrstuvwxyz"</span><span>;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">int</span><span> times = </span><span class="number">5000</span><span>;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> lstart1 = System.currentTimeMillis();</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String str = </span><span class="string">""</span><span>;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">for</span><span> (</span><span class="keyword">int</span><span> i = </span><span class="number">0</span><span>; i &lt; times; i++) {</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str += tempstr;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> lend1 = System.currentTimeMillis();</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> time = (lend1 - lstart1);</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(time);</span></li></ol></div><p>可惜我的计算机不是超级计算机，得到的结果每次不一定一样一般为 46687左右。<br />也就是46秒。<br />我们再看看以下代码</p><div class="highlighter"><ol class="highlighter-j"><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String tempstr = </span><span class="string">"abcdefghijklmnopqrstuvwxyz"</span><span>;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">int</span><span> times = </span><span class="number">5000</span><span>;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> lstart2 = System.currentTimeMillis();</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer sb = </span><span class="keyword">new</span><span> StringBuffer();</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">for</span><span> (</span><span class="keyword">int</span><span> i = </span><span class="number">0</span><span>; i &lt; times; i++) {</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sb.append(tempstr);</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> lend2 = System.currentTimeMillis();</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> time2 = (lend2 - lstart2);</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(time2);</span></li></ol></div><p>得到的结果为 16 有时还是 0<br />所以结论很明显，StringBuffer 的速度几乎是String 上万倍。当然这个数据不是很准确。因为循环的次数在100000次的时候，差异更大。不信你试试。</p><p> </p><p>根据上面所说：</p><p>str += "here";<br />的处理步骤实际上是通过建立一个StringBuffer,让侯调用append(),最后<br />再将StringBuffer toSting();</p><p>所以str += "here";可以等同于</p><p>StringBuffer sb = new StringBuffer(str);</p><p>sb.append("here");</p><p>str = sb.toString();</p><p>所以上面直接利用"+"来连接String的代码可以基本等同于以下代码</p><div class="highlighter"><ol class="highlighter-j"><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String tempstr = </span><span class="string">"abcdefghijklmnopqrstuvwxyz"</span><span>;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">int</span><span> times = </span><span class="number">5000</span><span>;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> lstart2 = System.currentTimeMillis();</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String str = </span><span class="string">""</span><span>;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">for</span><span> (</span><span class="keyword">int</span><span> i = </span><span class="number">0</span><span>; i &lt; times; i++) {</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer sb = </span><span class="keyword">new</span><span> StringBuffer(str);</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sb.append(tempstr);</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str = sb.toString();</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> lend2 = System.currentTimeMillis();</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="keyword">long</span><span> time2 = (lend2 - lstart2);</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(time2);</span></li></ol></div><p>平均执行时间为46922左右，也就是46秒。</p><p> </p><p>总结: 如果在程序中需要对字符串进行频繁的修改连接操作的话.使用StringBuffer性能会更高<br /><br />===========================================================================================<br /><br /><br /></p>自从Java 5.0发布以后，我们的比较列表上将多出一个对象了，这就是StringBuilder类。String类是不可变类，任何对String的改变都会引发新的String对象的生成；而StringBuffer则是可变类，任何对它所指代的字符串的改变都不会产生新的对象，可变和不可变类这一对对象已经齐全了，那么为什么还要引入新的StringBuilder类干吗？相信大家都有此疑问，我也如此。下面，我们就来看看引入该类的原因。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为什么会出现那么多比较String和StringBuffer的文章？</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 原因在于当改变字符串内容时，采用StringBuffer能获得更好的性能。既然是为了获得更好的性能，那么采用StringBuffer能够获得最好的性能吗？</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 答案是NO！ </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为什么？</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你读过《Think in Java》，而且对里面描述HashTable和HashMap区别的那部分章节比较熟悉的话，你一定也明白了原因所在。对，就是支持线程同步保证线程安全而导致性能下降的问题。<span style="color: red;">HashTable是线程安全的，很多方法都是synchronized方法，而HashMap不是线程安全的，但其在单线程程序中的性能比HashTable要高</span>。StringBuffer和StringBuilder类的区别也在于此，<span style="color: red;">新引入的StringBuilder类不是线程安全的，但其在单线程中的性能比StringBuffer高</span>。如果你对此不太相信，可以试试下面的例子：</p><p>package com.hct.test;</p><p>import java.util.ArrayList;<br />import java.util.Iterator;<br />import java.util.List;</p><p>/**<br /> * @author: chengtai.he<br /> * @created:2009-12-9 上午09:59:57<br /> */<br />public class StringBuilderTester {<br /> private static final String base = " base string. ";<br /> private static final int count = 2000000;</p><p> public static void stringTest() {<br />&nbsp; long begin, end;<br />&nbsp; begin = System.currentTimeMillis();<br />&nbsp; String test = new String(base);<br />&nbsp; for (int i = 0; i &lt; count/100; i++) {<br />&nbsp;&nbsp; test = test + " add ";<br />&nbsp; }<br />&nbsp; end = System.currentTimeMillis();<br />&nbsp; System.out.println((end - begin)<br />&nbsp;&nbsp;&nbsp; + " millis has elapsed when used String. ");<br /> }</p><p> public static void stringBufferTest() {<br />&nbsp; long begin, end;<br />&nbsp; begin = System.currentTimeMillis();<br />&nbsp; StringBuffer test = new StringBuffer(base);<br />&nbsp; for (int i = 0; i &lt; count; i++) {<br />&nbsp;&nbsp; test = test.append(" add ");<br />&nbsp; }<br />&nbsp; end = System.currentTimeMillis();<br />&nbsp; System.out.println((end - begin)<br />&nbsp;&nbsp;&nbsp; + " millis has elapsed when used StringBuffer. ");<br /> }</p><p> public static void stringBuilderTest() {<br />&nbsp; long begin, end;<br />&nbsp; begin = System.currentTimeMillis();<br />&nbsp; StringBuilder test = new StringBuilder(base);<br />&nbsp; for (int i = 0; i &lt; count; i++) {<br />&nbsp;&nbsp; test = test.append(" add ");<br />&nbsp; }<br />&nbsp; end = System.currentTimeMillis();<br />&nbsp; System.out.println((end - begin)<br />&nbsp;&nbsp;&nbsp; + " millis has elapsed when used StringBuilder. ");<br /> }</p><p> public static String appendItemsToStringBuiler(List list) {<br />&nbsp; StringBuilder b = new StringBuilder();</p><p>&nbsp; for (Iterator i = list.iterator(); i.hasNext();) {<br />&nbsp;&nbsp; b.append(i.next()).append(" ");<br />&nbsp; }</p><p>&nbsp; return b.toString();<br /> }</p><p> public static void addToStringBuilder() {<br />&nbsp; List list = new ArrayList();<br />&nbsp; list.add(" I ");<br />&nbsp; list.add(" play ");<br />&nbsp; list.add(" Bourgeois ");<br />&nbsp; list.add(" guitars ");<br />&nbsp; list.add(" and ");<br />&nbsp; list.add(" Huber ");<br />&nbsp; list.add(" banjos ");</p><p>&nbsp; System.out.println(StringBuilderTester.appendItemsToStirngBuffer(list));<br /> }</p><p> public static String appendItemsToStirngBuffer(List list) {<br />&nbsp; StringBuffer b = new StringBuffer();</p><p>&nbsp; for (Iterator i = list.iterator(); i.hasNext();) {<br />&nbsp;&nbsp; b.append(i.next()).append(" ");<br />&nbsp; }</p><p>&nbsp; return b.toString();<br /> }</p><p> public static void addToStringBuffer() {<br />&nbsp; List list = new ArrayList();<br />&nbsp; list.add(" I ");<br />&nbsp; list.add(" play ");<br />&nbsp; list.add(" Bourgeois ");<br />&nbsp; list.add(" guitars ");<br />&nbsp; list.add(" and ");<br />&nbsp; list.add(" Huber ");<br />&nbsp; list.add(" banjos ");</p><p>&nbsp; System.out.println(StringBuilderTester.appendItemsToStirngBuffer(list));<br /> }</p><p> public static void main(String[] args) {<br />&nbsp; stringTest();<br />&nbsp; stringBufferTest();<br />&nbsp; stringBuilderTest();<br />&nbsp; addToStringBuffer();<br />&nbsp; addToStringBuilder();<br /> }<br />}</p><p>上面的程序结果如下：<br />5266 millis has elapsed when used String. <br />375 millis has elapsed when used StringBuffer. <br />281 millis has elapsed when used StringBuilder. <br /> I&nbsp;&nbsp; play&nbsp;&nbsp; Bourgeois&nbsp;&nbsp; guitars&nbsp;&nbsp; and&nbsp;&nbsp; Huber&nbsp;&nbsp; banjos&nbsp; <br /> I&nbsp;&nbsp; play&nbsp;&nbsp; Bourgeois&nbsp;&nbsp; guitars&nbsp;&nbsp; and&nbsp;&nbsp; Huber&nbsp;&nbsp; banjos <br />从上面的结果来看，这三个类在单线程程序中的性能差别一目了然，采用String对象时，即使运行次数仅是采用其他对象的1/100，其执行时间仍然比其他对象高出25倍以上；而采用StringBuffer对象和采用StringBuilder对象的差别也比较明显，前者是后者的1.5倍左右。由此可见，如果我们的程序是在单线程下运行，或者是不必考虑到线程同步问题，我们应该优先使用StringBuilder类；当然，如果要保证线程安全，自然非StringBuffer莫属了。</p><p>除了对多线程的支持不一样外，这两个类的使用几乎没有任何差别，上面的例子就是个很好的说明。appendItemsToStringBuiler和appendItemsToStirngBuffer两个方法除了采用的对象分别为StringBuilder和StringBuffer外，其他完全相同，而效果也完全相同。</p><p><br /></p> 
<img src ="http://www.blogjava.net/achan2bj/aggbug/388410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/achan2bj/" target="_blank">achan2bj</a> 2012-09-24 09:44 <a href="http://www.blogjava.net/achan2bj/archive/2012/09/24/388410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>新开通BlogJava</title><link>http://www.blogjava.net/achan2bj/archive/2012/09/24/388405.html</link><dc:creator>achan2bj</dc:creator><author>achan2bj</author><pubDate>Mon, 24 Sep 2012 00:42:00 GMT</pubDate><guid>http://www.blogjava.net/achan2bj/archive/2012/09/24/388405.html</guid><wfw:comment>http://www.blogjava.net/achan2bj/comments/388405.html</wfw:comment><comments>http://www.blogjava.net/achan2bj/archive/2012/09/24/388405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/achan2bj/comments/commentRss/388405.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/achan2bj/services/trackbacks/388405.html</trackback:ping><description><![CDATA[新开通BlogJava，收到注册成功邮件其实不是今天，也是最近的事了。在这里记录与java有关或无关的事情，让自己心情增加多一个空间。<img src ="http://www.blogjava.net/achan2bj/aggbug/388405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/achan2bj/" target="_blank">achan2bj</a> 2012-09-24 08:42 <a href="http://www.blogjava.net/achan2bj/archive/2012/09/24/388405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>