<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>fuliang</title>
    <description></description>
    <link>http://fuliang.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
          <item>
        <title>EJB3感性认识</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/187744" style="color:red;">http://fuliang.javaeye.com/blog/187744</a>&nbsp;
          发表时间: 2008年04月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp;&nbsp;&nbsp; 面向对象方法课留了个作业，使用EJB实现一个简单的东东，从而更深刻的体会EJB组件。<br />虽然J2EE学了不少，但正统的EJB一直没有怎么看。今天大体看了看EJB3.0，感觉<br />EJB3.0在易用方面的确有很大的改进。曾经以极高的复杂性的EJB,现在和Spring、Hibernate<br />这些轻量级的解决方案相比已经差不了哪去了,EJB在轻量化，所有的Bean都可以是POJO的，只<br />需要使用注解表明是Entity bean,Stateless/Stateful Session bean,Message-driven bean,<br />Remote/Local接口,不需要实现特定的接口。Java Persistence已经从EJB中分离出来，作为一<br />个独立的标准JPA,而Hibernate是JPA的一个实现。这样Entity bean成为了可以脱离容器的普通<br />的POJO,JPA像JDBC那样可以在普通的应用程序的持久化中被使用。<br />&nbsp;&nbsp;&nbsp;&nbsp; EJB容器提供的基础性服务(声明性事物管理、安全、并发控制、JNDI等)，使得受管的Bean受益于<br />这些服务的同时，这些Bean可以脱离容器而被测试，这在测试先行，敏捷开发流行的时代，尤为重要。<br />&nbsp;&nbsp;&nbsp;&nbsp; EJB的资源管理为Bean进行了实例池化，这点感觉要比Spring依靠依赖注入的单例、原型等创建bean<br />相比具有更高的效率，像Stateless Session Bean、Message-Driven Bean都有若干个实例保存在池中，<br />可以同时相应多个请求，响应后在放入池中。而Stateful Session Bean则提供了钝化和激活操作，可<br />以在会话结束后钝化，将其状态保存在磁盘中，而在再次使用时，创建一个新的实例，将保存的状态拷<br />贝在这个新的实例中。这要比Spring简单的单例和原型要高效一些，当然随着JVM效率的提高，似乎对象<br />创建已经不太被看作是效率问题，像Web层的框架的应用控制器(Action)趋势是从单例到原型方式演进，<br />线程安全似乎更被重视。当然厂商对EJB容器的优化与直接Spring相比带来的优势是显然的。<br />&nbsp;&nbsp;&nbsp; &nbsp;EJB与这些轻量级框架相比最大的优势是透明的分布式对象，这对于需要分布式的应用来说是最大的亮<br />点。分布式对象可以分布在不同的服务器上，从而使得应用程序具有很好的伸缩性。提供JDNI来查找定位<br />服务，虽然JDNI的lookup方式的定位服务没有依赖注入方式先进，但JDNI可以提供跨网络的服务定位。<br />&nbsp;&nbsp;&nbsp;&nbsp; 总体感觉EJB3和Spring等轻量级的解决方案相比，已经没有那么复杂的，EJB在轻量化，变得更易<br />用。Seam是个使用EJB3.0组合JSF的一站式的开发框架，整个的理念还很先进,不过入门门槛比较高。<br />对于使用EJB3来说Seam是个不错的选择。<br />&nbsp; </p>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/187744#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 28 Apr 2008 19:49:50 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/187744</link>
        <guid>http://fuliang.javaeye.com/blog/187744</guid>
      </item>
          <item>
        <title>Java开源搜索引擎[收藏]</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/186114" style="color:red;">http://fuliang.javaeye.com/blog/186114</a>&nbsp;
          发表时间: 2008年04月24日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h4 onclick="DictFold('PWDECMEC1');">
<div class="d_left"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" />&nbsp;Egothor&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC1" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Egothor是一个用Java编写的开源而高效的全文本搜索引擎。借助Java的跨平台特性，Egothor能应用于任何环境的应用，既可配置为单独的搜索引擎，又能用于你的应用作为全文检索之用。
<p><a href="http://www.open-open.com/open24132.htm"><span style="color: #336699;">更多Egothor信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC2');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Nutch&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC2" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Nutch&nbsp;是一个开源Java&nbsp;实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。包括全文搜索和Web爬虫。
<p><a href="http://www.open-open.com/open24232.htm"><span style="color: #336699;">更多Nutch信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC3');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Lucene&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC3" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Apache Lucene是一个基于Java全文搜索引擎，利用它可以轻易地为Java软件加入全文搜寻功能。Lucene的最主要工作是替文件的每一个字作索引，索引让搜寻的效率比传统的逐字比较大大提高，Lucen提供一组解读，过滤，分析文件，编排和使用索引的API，它的强大之处除了高效和简单外，是最重要的是使使用者可以随时应自已需要自订其功能。
<p><a href="http://www.open-open.com/open24332.htm"><span style="color: #336699;">更多Lucene信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC4');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Oxyus&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC4" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">是一个纯java写的web搜索引擎。
<p><a href="http://www.open-open.com/open24432.htm"><span style="color: #336699;">更多Oxyus信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC5');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;BDDBot&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC5" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">BDDBot是一个简单的易于理解和使用的搜索引擎。它目前在一个文本文件(urls.txt)列出的URL中爬行，将结果保存在一个数据库中。它也支持一个简单的Web服务器，这个服务器接受来自浏览器的查询并返回响应结果。它可以方便地集成到你的Web站点中。
<p><a href="http://www.open-open.com/open24532.htm"><span style="color: #336699;">更多BDDBot信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC6');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Zilverline&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC6" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Zilverline是一个搜索引擎，它通过web方式搜索本地硬盘或intranet上的内容。Zilverline可以从PDF, Word, Excel, Powerpoint, RTF, txt, java, CHM,zip, rar等文档中抓取它们的内容来建立摘要和索引。从本地硬盘或intranet中查找到的结果可重新再进行检索。Zilverline支持多种语言其中包括中文。
<p><a href="http://www.open-open.com/open71032.htm"><span style="color: #336699;">更多Zilverline信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC7');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;XQEngine&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC7" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">XQEngine用于XML文档的全文本搜索引擎.利用XQuery做为它的前端查询语言.它能够让你查询XML文档集合通过使用关键字的逻辑组合.有点类似于Google与其它搜索引擎搜索HTML文档一样.XQEngine只是一个用Java开发的很紧凑的可嵌入的组件.
<p><a href="http://www.open-open.com/open87632.htm"><span style="color: #336699;">更多XQEngine信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC8');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;MG4J&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC8" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">MG4J可以让你为大量的文档集合构建一个被压缩的全文本索引,通过使内插编码(interpolative coding)技术.
<p><a href="http://www.open-open.com/open87932.htm"><span style="color: #336699;">更多MG4J信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC9');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;JXTA Search&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC9" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">JXTA Search是一个分布式的搜索系统.设计用在点对点的网络与网站上.
<p><a href="http://www.open-open.com/open88032.htm"><span style="color: #336699;">更多JXTA Search信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC10');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;YaCy&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC10" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">YaCy基于p2p的分布式Web搜索引擎.同时也是一个Http缓存代理服务器.这个项目是构建基于p2p Web索引网络的一个新方法.它可以搜索你自己的或全局的索引,也可以Crawl自己的网页或启动分布式Crawling等.<br />
<p><a href="http://www.open-open.com/open104432.htm"><span style="color: #336699;">更多YaCy信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC11');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Red-Piranha&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC11" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Red-Piranha是一个开源搜索系统,它能够真正"学习"你所要查找的是什么.Red-Piranha可作为你桌面系统(Windows,Linux与Mac)的个人搜索引擎,或企业内部网搜索引擎,或为你的网站提供搜索功能,或作为一个P2P搜索引擎,或与wiki结合作为一个知识/文档管理解决方案,或搜索你要的RSS聚合信息,或搜索你公司的系统(包括SAP,Oracle或其它任何Database/Data source),或用于管理PDF,Word和其它文档,或作为一个提供搜索信息的WebService或为你的应用程序(Web,Swing,SWT,Flash,Mozilla-XUL,PHP, Perl或c#/.Net)提供搜索后台等等.
<p><a href="http://www.open-open.com/open115132.htm"><span style="color: #336699;">更多Red-Piranha信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC12');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;LIUS&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC12" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">LIUS是一个基于Jakarta Lucene项目的索引框架。LIUS为Lucene添加了对许多文件格式的进行索引功能如：<br />Ms Word,Ms Excel,Ms PowerPoint,RTF,PDF,XML,HTML,TXT,Open Office序列和JavaBeans。针对JavaBeans的索引特别有用当我们要对数据库进行索引或刚好用户使用持久层ORM技术如：Hibernate,JDO,Torque,TopLink进行开发时。
<p><a href="http://www.open-open.com/open126232.htm"><span style="color: #336699;">更多LIUS信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC13');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Aperture&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC13" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Aperture这个Java框架能够从各种各样的资料系统(如：文件系统、Web站点、IMAP和Outlook邮箱)或存在这些系统中的文件(如:文档、图片)爬取和搜索其中的全文本内容与元数据。它当前支持的文件格式如下： <br />
<li>Plain text </li>
<li>HTML, XHTML </li>
<li>XML </li>
<li>PDF (Portable Document Format) </li>
<li>RTF (Rich Text Format) </li>
<li>Microsoft Office: Word, Excel, Powerpoint, Visio, Publisher </li>
<li>Microsoft Works </li>
<li>OpenOffice 1.x: Writer, Calc, Impress, Draw </li>
<li>StarOffice 6.x - 7.x+: Writer, Calc, Impress, Draw </li>
<li>OpenDocument (OpenOffice 2.x, StarOffice 8.x) </li>
<li>Corel WordPerfect, Quattro, Presentations </li>
<li>Emails (.eml files)
<p><a href="http://www.open-open.com/open142232.htm"><span style="color: #336699;">更多Aperture信息</span></a></p>
</li>
</div>
<h4 onclick="DictFold('PWDECMEC14');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Apache Solr&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC14" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Solr是一个高性能，采用Java5开发，基于Lucene的全文搜索服务器。文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过http收到一个XML/JSON响应来实现。它的主要特性包括：高效、灵活的缓存功能，垂直搜索功能，高亮显示搜索结果，通过索引复制来提高可用性，提供一套强大Data Schema来定义字段，类型和设置文本分析，提供基于Web的管理界面等。
<p><a href="http://www.open-open.com/open193032.htm"><span style="color: #336699;">更多Apache Solr信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC15');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Paoding&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC15" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Paoding中文分词是一个使用Java开发的，可结合到Lucene应用中的，为互联网、企业内部网使用的中文搜索引擎分词组件。Paoding填补了国内中文分词方面开源组件的空白，致力于此并希翼成为互联网网站首选的中文分词开源组件。 Paoding中文分词追求分词的高效率和用户良好体验。
<p><a href="http://www.open-open.com/open201232.htm"><span style="color: #336699;">更多Paoding信息</span></a></p>
</div>
<h4 onclick="DictFold('PWDECMEC16');">
<div class="d_left"><span style="color: #336699;"><img src="http://www.open-open.com/resource/icon_1.gif" alt="" /></span>&nbsp;Carrot2&nbsp;</div>
</h4>
<div id="dictc_PWDECMEC16" style="padding-right: 15px; padding-left: 15px; padding-bottom: 5px; padding-top: 1px;">Carrot2是一个开源搜索结果分类引擎。它能够自动把搜索结果组织成一些专题分类。Carrot2提供的一个架构能够从各种搜索引擎（YahooAPI、GoogleAPI、MSN Search API、eTools Meta Search、Alexa Web Search、PubMed、OpenSearch、Lucene index、SOLR）获取搜索结果。</div>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/186114#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 24 Apr 2008 00:09:02 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/186114</link>
        <guid>http://fuliang.javaeye.com/blog/186114</guid>
      </item>
          <item>
        <title>动态语言很好，很强大！</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/185176" style="color:red;">http://fuliang.javaeye.com/blog/185176</a>&nbsp;
          发表时间: 2008年04月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          前几天看了徐老师写的<a href="http://hi.baidu.com/xuqingyang/blog/item/665b9601b70b7e031d9583a8.html" target="_blank">谈谈学习动态脚本语言对搞web mining研究的必要性</a>，想起上次英语课话剧，使用ruby一行代码完成任务的事，深刻体会到动态语言的简洁强大之处，对处理文本的方便之处。开始剧本中是为了更好的理解剧情，中英文混在一起的，后来剧情比较熟了，为了看台词方便，希望把英语提取出来，我们这组幸好有我是计算机出身的，否则得花一晚上手工完成了。<br />格式类似如下：<br /><div class="quote_title">引用</div><div class="quote_div"><br /> 在一旁看的七濑美雪以被打败的表情说话：「都是因为阿一你擅自在滑雪练习场之外的地方下来，所以才会变成这样啊。」 That's all because you had to go beyond the training grounds to ski.<br />还说咧！下大雪使得升降椅停止运转，如果照那样直直下去，根本就到不了山庄。同一座山，只要斜斜地穿越过去，应该就可以回到国民旅馆。」 Stop talking please,If the cable cars stop we won't be able to get back to the hotel. This way!<br />阿一边说边把鼻子上的雪块擦掉。 <br />「我却不那麽认为。」 I don't think so<br />美雪愈来愈听不下去：「——通常都是先滑到山下，然後搭计程车回国民旅馆。你从小就喜欢和别人唱反调，而且顽固得要命…」 We always go to the bottom of the hill,and take a cab back to the hotel. you are always so stubborn.<br />「真是罗哩八嗦，每次只要和你出来旅行，总是会遇到灾难、或是被卷入奇怪的事件」 <br /> Oh you’re so troublesome we get into trouble every time we travel,or get involved in some weird situation.<br />「阿一，你说什麽！这句话应该是我说的才对吧：以前就没有发生过这种事吗？你提议要去陌生的地力滑雪，不仅差一点遇难，最後躲进小木尾里，结果在那里面发生杀人事件」 <br /> I think I shooud say that! You are the the one who wanted to go skiing in a unknown place,the last time this happened we got involved in a murder!<br />「喂，美雪！我们太幸运了！」 Hey May, we're so lucky!<br />「阿一，你有在听我说话吗？」 Are you listening?<br />「别说了，你看那栋房子！」 Oh look, there's a house!<br /> 阿一用滑雪杖指着斜面下方一栋有红色屋顶的房子。 <br />「咦？可是刚才我们看到的别墅，里面都没有住人啊。这一带的别墅，一到了冬季，几乎都没有人使用…」 But we saw a house a while before, most of the houses in the area are empty during the winter.<br />阿一笑着对不抱希望的美雪说：「没问题的啦。你看冰柱就知道了嘛。」 That’s no problem, look at the icicles.<br />「冰柱？」 Icicles?<br />「我们之前所见到的无人别墅，屋檐下的冰柱并没有像现在这栋这样巨大吧？」 The empty house we saw, didn't have icicles this size, did it?<br />「这麽说来…不过，那又怎样？」 Yes, but what if they didn't?<br />「冰柱这玩意的形成是靠屋顶上的积雪融化滴下来，再因为天气冷而结冻所形成的。有巨大的冰柱表示说，这栋房子因为有热气，所以屋顶的积雪才会融化而形成冰柱。」 <br /> Icicles are made fro the melting snow,only a house with enough heat would have icicles this size.<br />「哦、原来如此」 Oh, I get it.<br />「我们走吧！那里面一定有暖气。」 Lets go,it must be warm inside.<br />「嗯。」 OK!<br />...<br /></div><br />一行ruby代码就可以搞定了：<br /><pre name="code" class="ruby">
File.read('script.txt').scan(/[\s\?\.,'!]*\w+[\s\?\.,'!]*/m).each{|w| print w } 
</pre><br />结果：<br /><div class="quote_title">引用</div><div class="quote_div"><br />That's all because you had to go beyond the training grounds to ski.<br /> Stop talking please,If the cable cars stop we won't be able to get back to the hotel. This way!<br /> I don't think so<br /> We always go to the bottom of the hill,and take a cab back to the hotel. you are always so stubborn.<br /> <br /> Oh youre so troublesome we get into trouble every time we travel,or get involved in some weird situation.<br /> <br />I think I shooud say that! You are the the one who wanted to go skiing in a unknown place,the last time this happened we got involved in a murder!<br /> Hey May, we're so lucky!<br /> Are you listening?<br /> Oh look, there's a house!<br /> But we saw a house a while before, most of the houses in the area are empty during the winter.<br /> Thats no problem, look at the icicles.<br /> Icicles?<br /> The empty house we saw, didn't have icicles this size, did it?<br /> Yes, but what if they didn't?<br /> <br />Icicles are made fro the melting snow,only a house with enough heat   would have icicles this size.<br /> Oh, I get it.<br /> Lets go,it must be warm inside.<br /> OK!<br /></div><br />再稍加排版就可以了。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/185176#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 21 Apr 2008 21:16:22 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/185176</link>
        <guid>http://fuliang.javaeye.com/blog/185176</guid>
      </item>
          <item>
        <title>打算写写Unix/Linux编程的学习笔记</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/185038" style="color:red;">http://fuliang.javaeye.com/blog/185038</a>&nbsp;
          发表时间: 2008年04月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          　　　这个学期从图书馆借了两本Unix/Linux编程的E文书，《Advanced Programming in the Unix Enviornment》（传说中的Unxi圣经级别的书简称APUE）和《Unix System Programming》。大体快看了一半，书这东东，看的快忘的也快，<a href="http://hi.baidu.com/david_jlu" target="_blank">David同学笔记</a>做得很好，很值得借鉴，现在也打算写写笔记。感觉操作系统这东东，好好学学的确很有必要，本科的时候操作系统的原理倒学了不少，当时老师没有推荐一本Unix/Linux的书籍看看，感觉很是可惜。现在同学中不少使用Ubuntu的，受其良好的熏陶，现在基本可以在Ubuntu下混了。<br /><br /><br />　　　
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/185038#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 21 Apr 2008 16:23:09 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/185038</link>
        <guid>http://fuliang.javaeye.com/blog/185038</guid>
      </item>
          <item>
        <title>Java Persistence with Hibernate</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/184443" style="color:red;">http://fuliang.javaeye.com/blog/184443</a>&nbsp;
          发表时间: 2008年04月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          以前看过英文版的若干个章节，Hibernate之父亲自执笔，感觉写的相当不错，同学实验室要买书，刚好出了中文版的，推荐了这本，现在在我手里，可以准备好好看看，很厚的一本书，翻译过来才600多页，有点出乎我的意料了，有阿敏司令把关，相信翻译水平应该不错。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/184443#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 19 Apr 2008 18:09:02 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/184443</link>
        <guid>http://fuliang.javaeye.com/blog/184443</guid>
      </item>
          <item>
        <title>用ruby写了一个搜索下载歌曲的工具</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/182868" style="color:red;">http://fuliang.javaeye.com/blog/182868</a>&nbsp;
          发表时间: 2008年04月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://fuliang.javaeye.com/blog/176323" target="_blank">前几天用java写了一个GUI的搜索下载工具</a>,主要利用baidu mp3搜索的结果。<a href="http://hi.baidu.com/david_jlu/blog/item/d76622a1f1a6878f46106446.html" target="_blank">david同学用perl写了命令行的类似的下载工具</a>,为了练练ruby,我又写了ruby版的。<br />Fetcher类：<br />根据url来Fetch到页面，供Parser分析之用<br /><pre name="code" class="ruby">
 require "net/http" 

 class Fetcher
  
  def fetch(url)
    host = url.scan(/\/\/(.*?)\//m)[0][0]
    path = url.split(/#{host}\//)[1]
   # print "host: ",host,"\n"
   # print "path: ",path,"\n"
    h = Net::HTTP.new(host,80)
    resp = h.get("/#{path}",nil)
   
    if resp.message == "OK"
     # puts "建立连接成功..." 
      return resp.body     
    end 
    return ""
  end

end
</pre><br /><br />Parser类：<br />提取出可供下载的链接，并通过ping，来选取速度最快的连接，供Download之用：<br /><pre name="code" class="ruby">
class Parser
public
  def initialize()
    @fetcher = Fetcher.new
  end

  def parse_mp3(html)
    urls = html.scan(/&lt;a href="(.*?)"/m)
    download_hosts_urls = {}
    parse_threads = []
    for url in urls do
        if url[0] =~ /.*?\.mp3,,.*?/
           parse_threads &lt;&lt; Thread.new(url) do |url|
              song_url = url[0].gsub(" ","%20")
              download_url = parse_download_url(song_url)
              if download_url
              	host =  download_url.scan(/\/\/(.*?)\//m)[0][0] 
              	#We only want to find the best download url,so we needn't care duplicate key
              	download_hosts_urls[host] = download_url
              end 
           end
        end
    end
    parse_threads.each{|t| t.join}
    puts "已经搜索到#{download_hosts_urls.size}个链接可以下载..."
    exit(1) if download_hosts_urls.size == 0
    puts "正在选择速度最快的链接..."
    host = select_best_host(download_hosts_urls.keys)
    download_hosts_urls[host]
  end

private
  def select_best_host(hosts)
    times_hosts = {}
    threads = []
    hosts.each do |host|
      threads &lt;&lt; Thread.new(host) do |host|
           response = `ping -c 1 -W 30 #{host}` #use`ping -n 1 -w 30 #{host}` in windows
           r_t = response.scan(/time=(\d+)/m) #only get integer part
           times_hosts[r_t[0][0]] = host unless r_t.empty? #duplicate key no problem 
      end
    end
   
    threads.each{|t| t.join}
   
    times = times_hosts.keys
    min = times.min
    times_hosts[min]
  end

  def parse_download_url(song_url)
     html = @fetcher.fetch(song_url)
     urls = html.scan(/&lt;a href="(.*?)"/m)
     return nil if urls.empty? || urls[0][0] =~ /.*?\.html/
     return urls[0][0]      
  end
end
</pre><br /><br />Download类：<br /><pre name="code" class="ruby">
 require "open-uri"
require "parser"
require "fetcher"

class Download
public
  def initialize(song_name)
    @song_name = song_name
    @search_url = "http://mp3.baidu.com/m?f=ms&tn=baidump3&ct=134217728&lf=&rn=&word=#@song_name&lm=0"
    @parser = Parser.new
    @fetcher = Fetcher.new
  end
 
  def download
    puts "正在建立连接..."
    html = @fetcher.fetch(@search_url)
    puts "正在获取搜索结果..."
    url = @parser.parse_mp3(html)
    puts "已经获得最快的下载连接:#{url}.\n开始下载..."
    doDownload(url)    
    puts "下载完毕..."
  end
private
  def doDownload(url)
    open(url) do |fin|
  	size = fin.size
  	download_size = 0
  	puts "大小: #{size / 1024}KB"
  	filename = url[url.rindex('/')+1, url.length-1]
  	puts "歌曲名: #{filename}"
  	open(File.basename("./#{filename}"),"wb") do |fout|
     	    while buf = fin.read(1024) do
       		fout.write buf
       		download_size += buf.size
                print "已经下载： #{download_size * 100 / size}%\r"
                STDOUT.flush 
           end
       end
    end 
    puts
  end
end

download = Download.new(ARGV[0])
download.download
</pre><br /><div class="quote_title">引用</div><div class="quote_div"><br />fuliang@fuliang-desktop:~/program/ruby/mp3download$ ruby download.rb pretty body<br />正在建立连接...<br />正在获取搜索结果...<br />已经搜索到25个链接可以下载...<br />正在选择速度最快的链接...<br />已经获得最快的下载连接:http://www.jxggzp.com/muisc/20051122185348.mp3.<br />开始下载...<br />大小: 6570KB<br />歌曲名: 20051122185348.mp3<br />已经下载： 100%<br />下载完毕...<br /></div><br />基本上可以使用。现在还存在一些问题，下载链接中有中文，往往会失败，主要是没有进行编码，知道ruby有个Iconv.conv来转换编码，不知道如何直接对中文进行编码：不知道没有像encode("gb2312","大海")之类的方法。另一个是下载问题：进度条有问题，主要open-uri使用open貌似就把文件下载到本地了，造成open很长时间，fin.read,fout.write是本地操作则非常快，结果下载进度从开始出现到下载完成瞬间就完成。希望各位达人可以帮助修正两个问题。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/182868#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 15 Apr 2008 12:56:50 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/182868</link>
        <guid>http://fuliang.javaeye.com/blog/182868</guid>
      </item>
          <item>
        <title>ruby下载文件的问题</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/182680" style="color:red;">http://fuliang.javaeye.com/blog/182680</a>&nbsp;
          发表时间: 2008年04月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我使用open-uri来下载文件：<br /><pre name="code" class="ruby">
require 'open-uri'

url = "http://www.union-ms.com/wenj/2005628171127.mp3"
open(url) do |fin|
  size = fin.size
  download_size = 0
  puts "size: #{size}"
  filename = url[url.rindex('/')+1, url.length-1]
  puts "name: #{filename}"
  open(File.basename("./#{filename}"),"wb") do |fout|
     while buf = fin.read(1024) do
       fout.write buf
       download_size += 1024
       #sleep(0.5)
       print "Downloaded #{download_size * 100 / size}%\r"
       STDOUT.flush 
    end
  end
end
</pre><br />下载像图片比较小的文件时，没有问题，当下载mp3这样的大文件时，很长时间都建立不起连接，<br />而那个连接在浏览器上打开很快，不知道什么原因？不知道有没有别的比较好的下载方法
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/182680#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 14 Apr 2008 19:01:03 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/182680</link>
        <guid>http://fuliang.javaeye.com/blog/182680</guid>
      </item>
          <item>
        <title>使用Struts2+Hibernate+Spring写了一个RSS Reader</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/179905" style="color:red;">http://fuliang.javaeye.com/blog/179905</a>&nbsp;
          发表时间: 2008年04月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          一个简单的RSS Reader其实很好写，或许根本不需要使用SSH2这把牛刀,使用jsp+servlet+javabean会变得及其简单，很快就可以搞定。其实这个<br />是面试实习生后的一个homework,所以尽量使用更多的技术，当<br />然也没要求必须的做，现在做的差不多了，不过在给我offer之前，貌似没<br />有地方拿给他们看了，还是弄到博客上吧。使用了sun的rome操作RSS.<br />lib太多了，附件放不下，.war文件都20多M,所以只把源代码放上,需要自己导入<br />spring2.0,hibernate3.2,struts2,rome，mysql-connector-java-5.1.5-bin.ja相关的包。<br /><img src="http://fuliang.javaeye.com/upload/picture/pic/11265/53569b0e-134e-31fa-9555-bdfa6932b0e7.bmp" />
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/179905#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 06 Apr 2008 22:11:56 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/179905</link>
        <guid>http://fuliang.javaeye.com/blog/179905</guid>
      </item>
          <item>
        <title>写了一个支持搜索并下载歌曲的工具</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/176323" style="color:red;">http://fuliang.javaeye.com/blog/176323</a>&nbsp;
          发表时间: 2008年03月26日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          主要是利用baidu搜索的到的结果,然后从页面中抽取歌曲的链接和与歌曲相关的其他信息,<br />然后利用这些链接进行下载.<br />感觉实用性还挺强的,虽然下载速度和迅雷没法比,貌似比直接下载.<br /><img src=" http://fuliang.javaeye.com/upload/picture/pic/10417/e3c1df74-1da3-3e3a-af10-ac7a779ba780.bmp " />
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/176323#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 26 Mar 2008 14:45:02 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/176323</link>
        <guid>http://fuliang.javaeye.com/blog/176323</guid>
      </item>
          <item>
        <title>Java正则表达式(二)</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/175035" style="color:red;">http://fuliang.javaeye.com/blog/175035</a>&nbsp;
          发表时间: 2008年03月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>Matcher类:</strong><br />使用Matcher类,最重要的一个概念必须清楚:<strong>组(Group)</strong>,在正则表达式中<br />()定义了一个组,由于一个正则表达式可以包含很多的组,所以下面先说说怎么划分组的,<br />以及这些组和组的下标怎么对应的.<br />下面我们看看一个小例子,来说明这个问题<br /><div class="quote_title">引用</div><div class="quote_div">\w(\d\d)(\w+) </div><br />这个正则表达式有三个组:<br />整个\w(\d\d)(\w+) 是第0组 group(0)<br />(\d\d)是第1组 group(1)<br />(\w+)是第2组 group(2)<br />我们看看和正则表达式匹配的一个字符串x99SuperJava，<br />group(0)永远都是匹配整个表达式的字符串的那部分x99SuperJava<br />group(1)是第1组(\d\d)匹配的部分:99<br />group(2)是第二组(\w+)匹配的那部分SuperJava<br />下面我们写一个程序来验证一下：<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class RegexTest {
	public static void main(String[] args) {
		String regex = "\\w(\\d\\d)(\\w+)";
		String candidate = "x99SuperJava";
		
		Pattern p = Pattern.compile(regex);
		Matcher matcher = p.matcher(candidate);
		if(matcher.find()){
			int gc = matcher.groupCount();
			for(int i = 0; i &lt;= gc; i++)
				System.out.println("group " + i + " :" + matcher.group(i));
		}
	}
}
</pre><br />输出结果:<br /><div class="quote_title">引用</div><div class="quote_div">group 0 :x99SuperJava<br />group 1 :99<br />group 2 :SuperJava</div><br /><br />下面我们看看Matcher类提供的方法：<br /><strong>public Pattern pattern()</strong><br />这个方法返回了，创建Matcher的那个pattern对象。<br />下面我们看看一个小例子来说明这个结果<br /><pre name="code" class="java">
import java.util.regex.*;

public class MatcherPatternExample{
  public static void main(String args[]){
      test();
  }

  public static void test(){
     Pattern p = Pattern.compile("\\d");
     Matcher m1 = p.matcher("55");
     Matcher m2 = p.matcher("fdshfdgdfh");

     System.out.println(m1.pattern() == m2.pattern());
     //return true
  }
}
</pre><br /><strong>public Matcher reset()</strong><br />这个方法将Matcher的状态重新设置为最初的状态。<br /><strong>public Matcher reset(CharSequence input)</strong><br />重新设置Matcher的状态，并且将候选字符序列设置为input后进行Matcher,<br />这个方法和重新创建一个Matcher一样，只是这样可以重用以前的对象。<br /><strong>public int start()</strong><br />这个方法返回了，Matcher所匹配的字符串在整个字符串的的开始下标：<br />下面我们看看一个小例子<br /><pre name="code" class="java">
public class MatcherStartExample{
  public static void main(String args[]){
      test();
  }
  public static void test(){
     //create a Matcher and use the Matcher.start() method
     String candidateString = "My name is Bond. James Bond.";
     String matchHelper[] =
      {"          ^","                      ^"};
     Pattern p = Pattern.compile("Bond");
     Matcher matcher = p.matcher(candidateString);

     //Find the starting point of the first 'Bond'
      matcher.find();
      int startIndex = matcher.start();
      System.out.println(candidateString);
      System.out.println(matchHelper[0] + startIndex);

     //Find the starting point of the second 'Bond'
      matcher.find();
      int nextIndex = matcher.start();
      System.out.println(candidateString);
      System.out.println(matchHelper[1] + nextIndex);
}
</pre><br />输出结果：<br />My name is Bond. James Bond.<br />          ^11<br />My name is Bond. James Bond.<br />                      ^23<br /><strong>public int start(int group)</strong><br />这个方法可以指定你感兴趣的sub group,然后返回sup group匹配的开始位置。<br /><strong>public int end()</strong><br />这个和start()对应，返回在以前的匹配操作期间，由给定组所捕获子序列的最后字符之后的偏移量。<br />其实start和end经常是一起配合使用来返回匹配的子字符串。<br /><strong>public int end(int group)</strong><br />和public int start(int group)对应，返回在sup group匹配的子字符串最后一个字符在整个字符串下标加一<br /><strong>public String group()</strong><br />返回由以前匹配操作所匹配的输入子序列。<br />这个方法提供了强大而方便的工具，他可以等同使用start和end,然后对字符串作substring(start,end)操作。<br />看看下面一个小例子：<br /><pre name="code" class="java">
import java.util.regex.*;

public class MatcherGroupExample{
  public static void main(String args[]){
      test();
  }
  public static void test(){
      //create a Pattern
      Pattern p = Pattern.compile("Bond");

      //create a Matcher and use the Matcher.group() method
      String candidateString = "My name is Bond. James Bond.";
      Matcher matcher = p.matcher(candidateString);
      //extract the group
      matcher.find();
      System.out.println(matcher.group());
  }
}
</pre><br /><strong>public String group(int group)</strong><br />这个方法提供了强大而方便的工具，可以得到指定的group所匹配的输入字符串<br />应为这两个方法经常使用，同样我们看一个小例子：<br /><pre name="code" class="java">
import java.util.regex.*;

public class MatcherGroupParamExample{
  public static void main(String args[]){
      test();
  }
  public static void test(){
     //create a Pattern
      Pattern p = Pattern.compile("B(ond)");

     //create a Matcher and use the Matcher.group(int) method
     String candidateString = "My name is Bond. James Bond.";
     //create a helpful index for the sake of output
     Matcher matcher = p.matcher(candidateString);
     //Find group number 0 of the first find
      matcher.find();
      String group_0 = matcher.group(0);
      String group_1 = matcher.group(1);
      System.out.println("Group 0 " + group_0);
      System.out.println("Group 1 " + group_1);
      System.out.println(candidateString);

     //Find group number 1 of the second find
      matcher.find();
      group_0 = matcher.group(0);
      group_1 = matcher.group(1);
      System.out.println("Group 0 " + group_0);
      System.out.println("Group 1 " + group_1);
      System.out.println(candidateString);
  }
}
</pre><br /><br /><strong>public int groupCount()</strong><br />这个方法返回了，正则表达式的匹配的组数。<br /><strong>public boolean matches()</strong><br />尝试将整个区域与模式匹配。这个要求整个输入字符串都要和正则表达式匹配。<br />和find不同， find是会在整个输入字符串查找匹配的子字符串。<br /><strong>public boolean find()</strong><br />find会在整个输入中寻找是否有匹配的子字符串，一般我们使用find的流程：<br /><pre name="code" class="java">
 while(matcher.find()){
    //在匹配的区域，使用group,replace等进行查看和替换操作
 }
</pre><br /><strong>public boolean find(int start)</strong><br />从输入字符串指定的start位置开始查找。<br /><strong>public boolean lookingAt()</strong><br />基本上是matches更松约束的一个方法，尝试将从区域开头开始的输入序列与该模式匹配<br /><strong>public Matcher appendReplacement (StringBuffer sb, String replacement)</strong><br />你想把My name is Bond. James Bond. I would like a martini中的Bond换成Smith<br /><pre name="code" class="java">
StringBuffer sb = new StringBuffer();
String replacement = "Smith";
Pattern pattern = Pattern.compile("Bond");
Matcher matcher =pattern.matcher("My name is Bond. James Bond. I would like a martini.");
while(matcher.find()){
  matcher.appendReplacement(sb,replacement);//结果是My name is Smith. James Smith
}
</pre><br />Matcher对象会维护追加的位置，所以我们才能不断地使用appendReplacement来替换所有的匹配。<br /><strong>public StringBuffer appendTail(StringBuffer sb)</strong><br />这个方法简单的把为匹配的结尾追加到StringBuffer中。在上一个例子的最后再加上一句：<br />matcher.appendTail(sb);<br />结果就会成为My name is Smith. James Smith. I would like a martini.<br /><strong>public String replaceAll(String replacement)</strong><br />这个是一个更方便的方法，如果我们想替换所有的匹配的话，我们可以简单的使用replaceAll就ok了。<br />是：<br /><pre name="code" class="java">
while(matcher.find()){
  matcher.appendReplacement(sb,replacement);//结果是My name is Smith. James Smith
}
matcher.appendTail(sb);
</pre><br />的更便捷的方法。<br /><pre name="code" class="java">public String replaceFirst(String replacement)</pre><br />这个与replaceAll想对应很容易理解，就是只替换第一个匹配的。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/175035#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 23 Mar 2008 11:30:46 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/175035</link>
        <guid>http://fuliang.javaeye.com/blog/175035</guid>
      </item>
          <item>
        <title>Berkely DB Java Edition学习笔记一</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/175018" style="color:red;">http://fuliang.javaeye.com/blog/175018</a>&nbsp;
          发表时间: 2008年03月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Berkely DB对于高并发、要求速度快的应用来说是个不错的选择，mysql就是用BDB实现的(mysql的后台) ，mysql快，BDB比mysql还要快N倍。BDB是一种嵌入式的、非关系数据库，它与其他的关系数据库RMDBS不同,它没有提供SQL,而是提供了自己的访问接口。作为一种嵌入式的数据库，它是进程内模式的，也就是说它和应用程序在同一内存空间运行，所以速度要高很多，与嵌入式的数据库如Derby、HSQLDB(都是RMDBS的)相比，它效率更高，使用方法也有很大的不同。现在BDB以被Oracle收购。Berkely DB提供的文档Getting Started with　Berkeley DB Java Edition可以说是短小精悍（１１３页），入门相当不错。下面Get Start吧：<br /><strong>Environment：</strong><br />首先要接触的就是Environment了，使用它来open　database以及做一管理方面的事情.<br />创建Environment，还需要Environment的一些配置信息EnvironmentConfig。<br />下面是创建的过程：<br /><pre name="code" class="java">
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setAllowCreate(true);
myDbEnvironment = new Environment(new File("/export/dbEnv"),
envConfig);
</pre><br />其中EnvironmentConfig提供了许多配置参数，常用的有：<br />envConfig.setAllowCreate()//如果不存在的env的话，是否要创建一个新的<br />envConfig.setReadOnly()//是否为只读的<br />envConfig.setTransactional()//是否使用事务<br />参数都是boolean类型的<br />除了EnvironmentConfig外，还有EnvironmentMutableConfig，他实际是EnvironmentConfig的父类，使用他来配置在创建完Environment之后可以改变<br />的属性：<br />setCachePercent()//设置cache的大小占JVM　memory的百分比<br />setCacheSize()//设置cache的大小<br />setTxnNoSync()//事务提交是否将改变的记录写入磁盘<br />setTxnWriteNoSync()//事务提交是否将log写入磁盘<br />下面看一下使用EnvironmentMutableConfig的方法:<br /><pre name="code" class="java">
Environment myEnv = new Environment(new File("/export/dbEnv"), null);
EnvironmentMutableConfig envMutableConfig =
new EnvironmentMutableConfig();
envMutableConfig.setTxnNoSync(true);
myEnv.setMutableConfig(envMutableConfig);
</pre><br />Environment通过close来关闭，释放资源<br />下面看看Environment在管理方面的一些方法：<br />可以通过Environment获得EnvironmentStats，他提供了Environment一些状态信息，<br />例如使用<br /><pre name="code" class="java">
long cacheMisses = myEnv.getStats(null).getNCacheMiss();
</pre><br />我们可以获得cache未命中的次数，据此来调整cache的大小<br />可以同过Environment.getDatabaseNames()来获得Environment的数据库的名字：<br /><pre name="code" class="java">
List myDbNames = myDbEnv.getDatabaseNames();
for(int i=0; i &lt; myDbNames.size(); i++) {
System.out.println("Database Name: " + (String)myDbNames.get(i));
}
</pre><br />可以通过Environment.removeDatabase()来删除一个数据库：<br /><pre name="code" class="java">
String dbName = myDB.getDatabaseName();
myDB.close();
myDBEnv.removeDatabase(null,dbName);
</pre><br />可以使用Environment.renameDatabase()来重新命名一个数据库：<br /><pre name="code" class="java">
String dbName = myDB.getDatabaseName();
String dbNewName = new String(dbName + ".new", "UTF-8");
myDB.close();
myDBEnv.renameDatabase(null,dbName,dbNewName);
</pre><br />可以使用Environment.truncateDatabase()来删除数据库中的所有记录：<br /><pre name="code" class="java">
myEnv.truncate(null, // txn handle
myDatabase.getDatabaseName(), // database name
true//whether to return the count of deleted records
);
</pre><br />第三个参数是否返回删除的记录数，性能有很大不同。false的话会很快<br /><strong>Database:</strong><br />最重要的一些操作大多都在Database里了，和Environment一样，它也有许多<br />配置的选项DatabaseConfig,我们先看看选项：<br />DatabaseConfig.setAllowCreate()//不能存在的话是open操作否创建新的<br />DatabaseConfig.setBtreeComparator()//设置Btree的比较器<br />DatabaseConfig.setDuplicateComparator()//设置判断重复的比较器<br />DatabaseConfig.setSortedDuplicates()//是否允许重复的记录<br />DatabaseConfig.setExclusiveCreate()//设为true,如果当前数据库已存在，则open失败，也就是说open操作会导致一个新的数据库被创建，默认为false<br />DatabaseConfig.setReadOnly()//是否是只读的<br />DatabaseConfig.setTransactional()//是否使用事务<br />下面我们看看Database的使用流程：<br /><pre name="code" class="java">
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setAllowCreate(true);
myDbEnvironment = new Environment(new File("/export/dbEnv"), envConfig);
DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setAllowCreate(true);
myDatabase = myDbEnvironment.openDatabase(null,
"sampleDatabase",
dbConfig);
</pre><br />我们通过Environment的openDatabase来创建Database对象。使用完了Database使用<br />close方法来关闭数据库释放资源。<br /><strong>Database Records</strong><br />Database Record是保存在数据库的内容，包含Key和value两部分，他们都被封装成<br />DatabaseEntry，DatabaseEntry只能存放字节数组，所以只要能把Key和Value是什么<br />类型的，只要能转化成字节数组就可以被DatabaseEntry封装。基本类型JE都有对应的Binding,复杂的类型可以使用序列化和自定义binding来实现。<br />下那面我们看看一个使用方法：<br /><pre name="code" class="java">
String aKey = "key";
String aData = "data";
try {
DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
DatabaseEntry theData = new DatabaseEntry(aData.getBytes("UTF-8"));
} catch (Exception e) {
}
</pre><br />我们不应该依赖机器默认的编码，通常要指定特定的编码方法getBytes("UTF-8");<br />我们先看看怎么从数据库中读写记录：<br />通过Database.put()和Database.get()我们可以从数据库中读写记录<br />put:<br /><pre name="code" class="java">
String aKey = "myFirstKey";
String aData = "myFirstData";
try {
DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
DatabaseEntry theData = new DatabaseEntry(aData.getBytes("UTF-8"));
myDatabase.put(null, theKey, theData);
} catch (Exception e) {
}
</pre><br />get:<br /><pre name="code" class="java">
String aKey = "myFirstKey";
try {
DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
DatabaseEntry theData = new DatabaseEntry();

if (myDatabase.get(null, theKey, theData, LockMode.DEFAULT) ==
OperationStatus.SUCCESS) {

byte[] retData = theData.getData();
String foundData = new String(retData, "UTF-8");
System.out.println("For key: '" + aKey + "' found data: '" +
foundData + "'.");
} else {
  System.out.println("No record found for key '" + aKey + "'.");
}
} catch (Exception e) {
}
</pre><br />删除操作:<br /><pre name="code" class="java">
String aKey = "myFirstKey";
DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
myDatabase.delete(null, theKey);
</pre><br /><strong>使用BIND APIs来操作基本类型：</strong><br />我们可以使用JE提供的Bind Apis来操作数字类型和字符串类型：<br />以Long为例：<br />存储数据使用Bind Apis一般步骤如下：<br />1、通过EntryBinding binding =TupleBinding.getPrimitiveBinding(Long.class);<br />2、通过EntryBinding 把数据放到DatabaseEntry中：<br />myBinding.objectToEntry(data, dataEntry);<br />获取数据使用Bind Apis一般步骤如下：<br />1、通过EntryBinding binding =TupleBinding.getPrimitiveBinding(Long.class);<br />2、通过EntryBinding将Entry转换成Object Long theLong = (Long) myBinding.entryToObject(theData);<br />下面代码以测试的形式演示了整个过程：<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import java.io.File;

import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;

import junit.framework.TestCase;

public class PrimitiveBindingTest extends TestCase{
	private Environment env;
	private Database db;
	private String key = "akey";
	private Long data = 1234556633L;
	
	public void setUp()throws Exception{
		EnvironmentConfig envConfig = new EnvironmentConfig();
		envConfig.setAllowCreate(true);
		env = new Environment(new File("etc/dbEnv"),envConfig);
		DatabaseConfig dbConfig = new DatabaseConfig();
		dbConfig.setAllowCreate(true);
		db = env.openDatabase(null, "myDB", dbConfig);
		DatabaseEntry keyEntry = new DatabaseEntry(key.getBytes("UTF-8"));
		DatabaseEntry dataEntry = new DatabaseEntry();
		
		EntryBinding myBinding = TupleBinding.getPrimitiveBinding(Long.class);
	    myBinding.objectToEntry(data, dataEntry);
	    db.put(null, keyEntry, dataEntry);
	}
	
	public void testGet()throws Exception{
		DatabaseEntry keyEntry = new DatabaseEntry(key.getBytes("UTF-8"));
		DatabaseEntry dataEntry = new DatabaseEntry();
		EntryBinding binding = TupleBinding.getPrimitiveBinding(Long.class);
		db.get(null, keyEntry, dataEntry, LockMode.DEFAULT);
		Long l = (Long)binding.entryToObject(dataEntry);
		assertEquals(l,data);
	}
	public void tearDown()throws Exception{
		db.close();
		env.truncateDatabase(null, "myDB",false);
		env.close();
	}
	
}
</pre><br /><strong>序列化复杂的类型</strong><br />步骤如下：<br />1、要存储的对象的类需要实现java.io.Serializable<br />2、打开两个数据库，一个存放数据，另一个存放类的信息<br />3、实例化com.sleepycat.bind.serial.StoredClassCatalog对象<br />4、创建uses com.sleepycat.bind.serial.SerialBinding对象<br />5、使用SerialBinding把对象放到DatabaseEntry中<br />下面是使用一个能够完整描述这个过程的例子来说明这个过程：<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import java.io.File;

import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;

import junit.framework.TestCase;

public class SerializableTypeTest extends TestCase{
	private Person person;
	private Environment env;
	private Database db,classDB;
	private StoredClassCatalog classCatalog;
	
	public void setUp()throws Exception{
		person = new Person();
		person.setAge(12);
		person.setName("zhansan");
		person.setSex('m');
	    
		EnvironmentConfig envConfig = new EnvironmentConfig();
		envConfig.setAllowCreate(true);
		env = new Environment(new File("etc/dbEnv"),envConfig);
		DatabaseConfig dbConfig = new DatabaseConfig();
		dbConfig.setAllowCreate(true);
		db = env.openDatabase(null, "myDB", dbConfig);
		classDB = env.openDatabase(null, "classDB", dbConfig);
		classCatalog = new StoredClassCatalog(classDB);
		EntryBinding dataBinding = new SerialBinding(classCatalog,Person.class);
			
		DatabaseEntry keyEntry = new DatabaseEntry(person.getName().getBytes("UTF-8"));
		DatabaseEntry dataEntry = new DatabaseEntry();
		dataBinding.objectToEntry(person, dataEntry);
		db.put(null, keyEntry, dataEntry);
	}
	
	public void testGet()throws Exception{
		EntryBinding dataBinding = new SerialBinding(classCatalog,Person.class);
	  
		DatabaseEntry keyEntry = new DatabaseEntry(person.getName().getBytes("UTF-8"));
	    DatabaseEntry dataEntry = new DatabaseEntry();
	    db.get(null, keyEntry, dataEntry, LockMode.DEFAULT);
	    Person p = (Person)dataBinding.entryToObject(dataEntry);
	    assertEquals(p.getName(),person.getName());
	    assertEquals(p.getAge(),person.getAge());
	    assertEquals(p.getSex(), person.getSex());
	}
	
	public void tearDown()throws Exception{
		db.close();
		classDB.close();
		env.truncateDatabase(null, "myDB", false);
		env.truncateDatabase(null, "classDB", false);
		env.close();
	}
}
</pre><br />要存储的对象对应的类<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import java.io.Serializable;

public class Person implements Serializable{
	private String name;
	private int age;
	private char sex;

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public char getSex() {
		return sex;
	}

	public void setSex(char sex) {
		this.sex = sex;
	}
}
</pre><br /><strong>自定义元组绑定：</strong><br />存储复杂对象自定义元组绑定的步骤：<br />1、创建要存储的对象，这个对象的类没有必要实现Serializable接口：<br />2、扩展com.sleepycat.bind.tuple.TupleBinding来实现自定义的Binging<br />3、创建2步欻关键的自定义binding对象<br />4、将创建的对象是用自定义个binding放到DatabaseEntry中<br />5、使用put方法存入数据库<br />下面的例子说明了这个过程：<br />自定义Binging：<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;

public class PersonTupleBinding extends TupleBinding{

	@Override
	public Object entryToObject(TupleInput ti) {
		Person person = new Person();
		person.setName(ti.readString());
		person.setAge(ti.readInt());
		person.setSex(ti.readChar());
		return person;
	}

	@Override
	public void objectToEntry(Object obj, TupleOutput output) {
		Person person = (Person)obj;
		output.writeString(person.getName());
		output.writeInt(person.getAge());
		output.writeChar(person.getSex());
	}
}
</pre><br />put/get的使用过程：<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import java.io.File;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;

import junit.framework.TestCase;

public class CustomTupleBindingTest extends TestCase{
	private Person person;
	private Environment env;
	private Database db;
	
	public void setUp()throws Exception{
		person = new Person();
		person.setAge(12);
		person.setName("zhansan");
		person.setSex('m');
	    
		
		EnvironmentConfig envConfig = new EnvironmentConfig();
		envConfig.setAllowCreate(true);
		env = new Environment(new File("etc/dbEnv"),envConfig);
		DatabaseConfig dbConfig = new DatabaseConfig();
		dbConfig.setAllowCreate(true);
		db = env.openDatabase(null, "myDB", dbConfig);
		PersonTupleBinding binding = new PersonTupleBinding();
		DatabaseEntry keyEntry = new DatabaseEntry(person.getName().getBytes("UTF-8"));
		DatabaseEntry dataEntry = new DatabaseEntry();
		binding.objectToEntry(person, dataEntry);
		db.put(null, keyEntry, dataEntry);
		
	}
	
	public void testGet()throws Exception{
		PersonTupleBinding binding = new PersonTupleBinding();
	    
		DatabaseEntry keyEntry = new DatabaseEntry(person.getName().getBytes("UTF-8"));
	    DatabaseEntry dataEntry = new DatabaseEntry();
	    db.get(null, keyEntry, dataEntry, LockMode.DEFAULT);
	    Person p = (Person)binding.entryToObject(dataEntry);
	    assertEquals(p.getName(),person.getName());
	    assertEquals(p.getAge(),person.getAge());
	    assertEquals(p.getSex(), person.getSex());
	}
	public void tearDown()throws Exception{
		db.close();
		env.truncateDatabase(null, "myDB", false);
		env.close();
	}
}
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/175018#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 23 Mar 2008 10:47:44 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/175018</link>
        <guid>http://fuliang.javaeye.com/blog/175018</guid>
      </item>
          <item>
        <title>体验一下Spring2.5 Annotation-based-configration</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/173170" style="color:red;">http://fuliang.javaeye.com/blog/173170</a>&nbsp;
          发表时间: 2008年03月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Spring2.5 Annotation-based-configration大大简化了配置,用一个经典的HelloWorld程序来体验一下:<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import org.springframework.stereotype.Component;

@Component
public class MessageProvider {
	private String message = "Hello World!";

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}
</pre><br />我们只要在bean面前使用@Component注解就可以被Spring的IOC容器管理了.<br />在看看怎么把MessageProvider注入到MessageRender里面的:<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MessageRender {
	private MessageProvider messageProvider;

	@Autowired
	public void setMessageProvider(MessageProvider messageProvider) {
		this.messageProvider = messageProvider;
	}
	
	public void render(){
		System.out.println(messageProvider.getMessage());
	}
}
</pre><br />使用@Autowired注解,Spring就可以把messageProvider注入到MessageRender里面了,<br />默认是通过byType来自动织入的,可以结合@Autowired 和 @Qualifier 结合使用时，自动注入的策略就从 byType 转变成 byName 了.<br />然后在XML中,只需要很少的配置了:<br /><pre name="code" class="xml">
&lt;?xml version="1.0" encoding="UTF-8" ?>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    &lt;context:component-scan base-package="edu.jlu.fuliang"/>
&lt;/beans>
</pre><br />&lt;context:annotationconfig/> 将隐式地向 Spring 容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 这4个 BeanPostProcessor。<br />&lt;context:component-scan/> 还允许定义过滤器将基包下的某些类纳入或排除<br /><pre name="code" class="xml">
&lt;context:component-scan base-package="com.baobaotao">
    &lt;context:include-filter type="regex" 
        expression="edu\.jlu\.fuliang\.service\..*"/>
    &lt;context:exclude-filter type="aspectj" 
        expression="edu.jlu.fuliang.util..*"/>
&lt;/context:component-scan>
</pre><br />下面是一个测试代码:<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloWorld {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
		MessageRender render = (MessageRender) context.getBean("messageRender");
		render.render();
	}
}
</pre><br />可惜在如果 Bean 不是自己编写的类（如 JdbcTemplate、SessionFactoryBean 等），注释配置将无法实施.所以还的使用XML,这样将导致XML和Annotation混用,感觉有点<br />混乱.个人感觉Annotation能够很大程度减少配置的负担,但没有XML灵活,并且修改配置的<br />时候需要重新编译代码.有时候也不见得比XML简洁,例如使用 @Transactional 事务注释，没有 aop/tx 命名空间的事务配置灵活和简单.
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/173170#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 17 Mar 2008 21:22:10 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/173170</link>
        <guid>http://fuliang.javaeye.com/blog/173170</guid>
      </item>
          <item>
        <title>你构建灵敏的界面了么？</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/172378" style="color:red;">http://fuliang.javaeye.com/blog/172378</a>&nbsp;
          发表时间: 2008年03月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          今天帮同学调试程序,发现许多同学初写GUI程序共同的毛病,不能够构建反映灵敏的界面,<br />并由此导致一些问题的出现。或许今天遇到的问题，再加上<a href="http://fuliang.javaeye.com/blog/149340" target="_blank">Java GUI在循环中调用repaint的问题分析</a>能够对初学Java GUI编程同学有所帮助。<br />我同学写的程序，把有问题那部分抽离出来，表述为下面一段代码：<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class ProblemTest extends JFrame{
   private JTextArea textArea = new JTextArea(10,10);
   private JButton startButton = new JButton("Start");
     
   public ProblemTest(){
	   JPanel panel = new JPanel();
	   panel.setLayout(new BorderLayout());
	   panel.add(new JScrollPane(textArea),BorderLayout.CENTER);
	   panel.add(startButton,BorderLayout.NORTH);
	   startButton.addActionListener(new ActionListener(){
		public void actionPerformed(ActionEvent e) {
               　　　 ProblemTest.this.append();	
		}
	   });
	  add(panel);
	   setSize(300,300);
	   setVisible(true);
   }
  
   public void append() {
		for (int i = 0; i &lt; 20; i++) {
			textArea.append("Line " + i + "\n");
			try {
				Thread.currentThread().sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}  
   public static void main(String[] args) {
	   ProblemTest pt = new ProblemTest();
	   pt.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
}
</pre><br />这段代码本来想作的事情是，当按下开始按钮后，在textArea区域中每隔２００毫秒追加<br />一行信息。但结果却是在textArea中一下打印出来了。<br />我们分析一下这个问题的原因：<br />当按下开始按钮时，响应该事件，开始执行<br /><pre name="code" class="java">
public void actionPerformed(ActionEvent e) {
         ProblemTest.this.append();	
}
</pre><br />开始往textarea中每隔２００毫秒追加一行信息，但事实上textarea要想反映出这种变化，肯定要repaint()。但是由于按下开始按钮这个事件还没有响应完,其他通知<br />textarea重绘的事件在消息队列中排在按下开始按钮这个事件之后，这样当按下开始按钮这个事件响应完毕后，才会响应通知textarea重绘的事件，然而这时候所有的信息都已经追加到textArea中的，所以所有的信息一次全显现了，而不是一条一条显示。<br />通常解决这个问题的方法是，把追加到textarea的操作放在一个线程中单独去做，在响应<br />按下开始按钮这个事件的代码中把这个线程开启：<br /><pre name="code" class="java">
package edu.jlu.fuliang;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class ProblemTest extends JFrame{
   private JTextArea textArea = new JTextArea(10,10);
   private JButton startButton = new JButton("Start");
   private Thread thread =  null; 
   
   public ProblemTest(){
	   JPanel panel = new JPanel();
	   panel.setLayout(new BorderLayout());
	   panel.add(new JScrollPane(textArea),BorderLayout.CENTER);
	   panel.add(startButton,BorderLayout.NORTH);
	   startButton.addActionListener(new ActionListener(){
		public void actionPerformed(ActionEvent e) {
               // ProblemTest.this.append();	
			 thread.start();
		}
	   });
	   thread = new Thread(){
			  public void run(){
				  append();
			  }
	   };
	   add(panel);
	   setSize(300,300);
	   setVisible(true);
   }
  
   public void append() {
		for (int i = 0; i &lt; 20; i++) {
			textArea.append("Line " + i + "\n");
			try {
				Thread.currentThread().sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}  
   public static void main(String[] args) {
	   ProblemTest pt = new ProblemTest();
	   pt.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
}
</pre><br />细心的同学还会发现第一段代码，按钮按下等到所有的信息一次全显现了的时候才抬起，<br />这往往是界面不灵敏的表现。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/172378#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 16 Mar 2008 19:02:59 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/172378</link>
        <guid>http://fuliang.javaeye.com/blog/172378</guid>
      </item>
          <item>
        <title>学SSH2时写的入门例子</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/172345" style="color:red;">http://fuliang.javaeye.com/blog/172345</a>&nbsp;
          发表时间: 2008年03月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          一博友问我有没有以前学SSH2做的入门例子,找了两个放上去,有需要的可以下来看看.<br />我的CSDN博客有这个例子的介绍<br />DWR+Spring+hibernate的增删改查的例子<br /><a href="http://blog.csdn.net/fuliangliang/archive/2007/09/24/1798037.aspx" target="_blank">Spring+hibernate+DWR整合</a><br />Struts2+Spring+hibernate的增删改查的例子<br /><a href="http://blog.csdn.net/fuliangliang/archive/2007/09/26/1801178.aspx" target="_blank">Ajax+Struts2+Spring+Hibernate整合</a><br />附件上有这两个的源码
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/172345#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 16 Mar 2008 16:11:20 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/172345</link>
        <guid>http://fuliang.javaeye.com/blog/172345</guid>
      </item>
          <item>
        <title>写了一个简单的Java版的eval函数</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/172233" style="color:red;">http://fuliang.javaeye.com/blog/172233</a>&nbsp;
          发表时间: 2008年03月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          今天一同学做东西要用这个东东,就帮他写了一个,先转化成后缀表达式,然后再<br />计算.当然也可以直接计算中缀表达式,考虑到要多位数,就没那么做.<br />支持多位数的带括号的整数的加减乘除.<br /><br /><pre name="code" class="java">
package edu.jlu.fuliang;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class Eval {
   public int eval(String exp){
	   List&lt;String> list = infixExpToPostExp(exp);//转化成后缀表达式
	   return doEval(list);//真正求值
   }
   
   //遇到操作符压栈，遇到表达式从后缀表达式中弹出两个数，计算出结果，压入堆栈
   private int doEval(List&lt;String> list) {
	  Stack&lt;String> stack =  new Stack&lt;String>();
	  String element;
	  int n1,n2,result;
	  try{
		  for(int i = 0; i &lt; list.size();i++){
			  element = list.get(i);
			  if(isOperator(element)){
				  n1 = Integer.parseInt(stack.pop());
				  n2 = Integer.parseInt(stack.pop());
				  result = doOperate(n1,n2,element);
				  stack.push(new Integer(result).toString());
			 }else{
				 stack.push(element);
			 }
		  }
		  return Integer.parseInt(stack.pop());
	  }catch(RuntimeException e){
		  throw new IllegalExpressionException(e.getMessage()); 	  
	  }
   }
   
   private int doOperate(int n1, int n2, String operator) {
      if(operator.equals("+"))
    	  return n1 + n2;
      else if(operator.equals("-"))
    	  return n1 - n2;
      else if(operator.equals("*"))
    	  return n1 * n2;
      else
          return n1 / n2;
   }

   private boolean isOperator(String str){
	   return str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/");
   }
   
   private List&lt;String> infixExpToPostExp(String exp){//将中缀表达式转化成为后缀表达式
	   List&lt;String> postExp = new ArrayList&lt;String>();//存放转化的后缀表达式的链表
	   StringBuffer numBuffer = new StringBuffer();//用来保存一个数的
	   Stack&lt;Character> opStack = new Stack&lt;Character>();//操作符栈
	   char ch,preChar;
	   opStack.push('#');
	   try{
		   for(int i = 0; i &lt; exp.length();){
			   ch = exp.charAt(i);
			   switch(ch){
			   		case '+':
			   		case '-':
			   		case '*':
			   		case '/':
			   			preChar = opStack.peek();
//		    	如果栈里面的操作符优先级比当前的大，则把栈中优先级大的都添加到后缀表达式列表中
			   			while(priority(preChar) >= priority(ch)){
			   				postExp.add(""+preChar);
			   				opStack.pop();
			   				preChar = opStack.peek();
			   			}
			   			opStack.push(ch);
			   			i++;
			   			break;
			   		case '(':
//	            左括号直接压栈
			   			opStack.push(ch);
			   			i++;
			   			break;
			   		case ')':
//		    	右括号则直接把栈中左括号前面的弹出，并加入后缀表达式链表中
			   			char c = opStack.pop();
			   			while(c != '('){
			   				postExp.add("" + c);
			   				c = opStack.pop();
			   			}
			   			i++;
			   			break;
//		     #号，代表表达式结束，可以直接把操作符栈中剩余的操作符全部弹出，并加入后缀表达式链表中
			     case '#':
			    	 char c1;
			    	 while(!opStack.isEmpty()){
			    		 c1 = opStack.pop();
			    		 if(c1 != '#')
			    		   postExp.add("" + c1);
			    	 }
			    	 i++;
			    	 break;
	                          //过滤空白符
			     case ' ':
			     case '\t':
			    	 i++;
			    	 break;
//		    	 数字则凑成一个整数，加入后缀表达式链表中
			     default:
			    	 if(Character.isDigit(ch)){
			    		 while(Character.isDigit(ch)){
			    			 numBuffer.append(ch);
			    		     ch = exp.charAt(++i);
			    		 }
			             postExp.add(numBuffer.toString());
			             numBuffer = new StringBuffer();
			    	 }else{
			    		 throw new IllegalExpressionException("illegal operator");
			    	 }
			   }
		   }
	   }catch(RuntimeException e){
		   throw new IllegalExpressionException(e.getMessage()); 
	   }
	   return postExp;
   }
   
   private int priority(char op){//定义优先级
		switch(op){
	   	case'+':
	   	case'-':
	   		return 1;
	   	case'*':
	   	case'/':
	   		return 2;
	   	case'(':
	   	case'#':
	        return 0;
	   	}
		throw new IllegalExpressionException("Illegal operator");
  }
   
   public static void main(String[] args) {
	   Eval eval = new Eval();
	   int result = eval.eval("2+3+55*22+21*2+(3+2)*3+4*3+3*4#");
	   System.out.println(result);
   }
}

</pre><br /><br /><pre name="code" class="java">
package edu.jlu.fuliang;

public class IllegalExpressionException extends RuntimeException{
    
	public IllegalExpressionException(){
		
    }
    
    public IllegalExpressionException(String info){
    	super(info);
    }
}

</pre>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/172233#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 15 Mar 2008 21:11:14 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/172233</link>
        <guid>http://fuliang.javaeye.com/blog/172233</guid>
      </item>
          <item>
        <title>面向对象的原则、模式、语言及框架（五）</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/169968" style="color:red;">http://fuliang.javaeye.com/blog/169968</a>&nbsp;
          发表时间: 2008年03月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>Liskov替换原则</strong><br />我们前面说了开闭原则OCP,其背后的主要机制是抽象和多态,但在静态语言中(如c++,java),支持抽象和多态的关键机制之一便是继承,正是有了继承,我们才能够<br />抽象出接口/基类,然后在子类中实现继承而来的抽象方法,或覆写基类已实现的方法<br />来定制子类。这样我们才能只通过扩展来实现新增的功能。<br />但是按照什么规则，我们才能设计出最佳的继承层次呢，以及什么样才是最佳的继承体<br />系呢？Liskov替换原则回答了这个问题。<br />下面我们就看看什么是Liskov替换原则(LSP):<br />LSP:子类型必须能够替换成它们的基类型。<br />LSP的重要性是不言而喻的，例如：<br /><pre name="code" class="java">
void f(BaseType bt){
  //使用bt来做事情
}
</pre><br />这时候如果我们传递一个子类型SubType的对象st：f(st);<br />如果这时候f出现一个错误的行为，那么SubType就违反了LSP，同时也导致了对<br />OCP的违反，因为我们要想这个方法对SubType也产生正确的行为，我们需要重新<br />修改f，对特定SupType进行定制操作，以便得到正确的行为，这样f对BaseType<br />的子类就不封闭了<br />我们看看Bob大叔举的一个违反LSP的例子：<br /><pre name="code" class="java">
public class Rectangle{
  protected int width;
  protected int hight;
 
  public void setWidth(int width){
     this.width = width; 
  }
  public void setHight(int hight){
     this.hight = hight;
 }
 public int getWidth(){
    return width;
 }
 public int getHight(){
   return hight;
 }
}
</pre><br />我们经常说继承是"Is-a",而组合是"has-a".这样如果一个的对象对于另一个类的对象<br />满足"is-a"关系，那么就应该把这个新类从原来那个类继承而来。<br />正方形是个矩形，因此把Square类视为Rectangle类的子类应该是合理的。"Is"被认为是<br />面向对象设计(OOA)的基本技术之一。但这会产生微妙但极为应该重视的问题。<br />但我们首先看到width,height两个变量对于Square来说是一种浪费，一般情况下，这种浪费是无关紧要的。但是setHight和setWidth对于Square来说是不合适的，因为正方形的长和宽应该相同的，我们为了确保这点，我们可以复写这两个方法：<br /><pre name="code" class="java">
public class Square extends Rectangle{
//others
 public void setWidth(int width){
     super.setWidth(width);
     super.setHight(width);
 }
  public void setHight(int hight){
     super.setHight(hight);
     super.setWidth(hight);
 }
}
</pre><br />这样似乎就满足了数学意义上的正方形了吧，但是我们考虑下面的函数<br /><pre name="code" class="java">
void testArea(Rectangle r){
  r.setWidth(2);
  r.setHeight(3);
  assert(r.area()==6);
}
</pre><br />当我们向testArea传递Square对象时，断言就会失败，因为testArea的编写者<br />不会认为高度的改变，会影响宽度。方法testArea表明Rectangle和Square的结构<br />是脆弱的，Square不能够替换掉Rectangle,因此Square和Rectagle之间的关系是违反了<br />LSP。<br />LSP让我们得出了一个非常重要的结论：一个模型，如果孤立的看，并不能发现问题，模型<br />的有效性只能通过它的客户程序来表现。如果孤立的看，最后那个版本的模型时自相容的，<br />但是如果从基类做出一些合理假设的程序员的角度来看，这个模型是有问题的。<br /><br />但是有谁能知道使用者会做出怎样的假设呢？大多数的假设是很难预测的。事实上如果我们<br />试图去预测所有这种假设，我们所得到的系统将充满不必要复杂性的Bad Smell.所以通常最好的方法是之预测那些最明显的对于LＳＰ违反情况而推迟其他的预测。<br />真正原因：<br /><strong>IS-A是关于行为的</strong><br />Square和Rectangle这个显然合理的模型为什么会出现问题？毕竟Square就是Rectangle,<br />难道它们之间不存在is-a关系么？<br />对于不是testArea的编写者来说Square就是Rectangle是没有问题的，但对于testArea得编写者而言，Square对象绝对不是Square,应为Square的行为方式和testArea所期望的行为方式是不相容的。对象的行为方式才是软件真正关注的问题，LSP清楚地告诉我们，OOD中<br />is-a是对于行为方式而言的，行为方式的合理假设是客户程序所依赖的。<br />另外一个问题：<br />我们都知道Java的异常，子类不能比超类抛出更多的异常，这其实就是LSP原则。<br />结论：<br />OCP是OOD的核心原则，如果这个原则应用的有效，应用程序就会有更好的维护性、可重用性和健壮性，而LSP是OCP成为可能的主要原则之一，正是子类型的可替换性，才使得使用基类型的模块无需修改的情况下就能进行扩展。这种可替换性是开发人员可以隐式依赖的东西。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/169968#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 10 Mar 2008 20:47:44 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/169968</link>
        <guid>http://fuliang.javaeye.com/blog/169968</guid>
      </item>
          <item>
        <title>Java正则表达式(一)</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/169946" style="color:red;">http://fuliang.javaeye.com/blog/169946</a>&nbsp;
          发表时间: 2008年03月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          正则表达式在处理文本方面用处非常大，最早像在Perl和awk语言中，提供了这种机制，Java在Java 2中也增加了正则表达式这个包java.util.regex。这个包为用户使用正则表达式，提供了易用而全面的支持。我的研究方向是web挖掘。从网页中提取内容,处理文本，当然需要正则表达式这个强大的工具了。<br />一、首先我们看一下怎么使用正则表达式的一个例子：<br />A Matcher examines the results of applying a pattern.<br />我们希望从这句话中找到所有开头为a的单词。<br />当然这只是一个简单的例子，你可以使用String提供的split方法，得到单词数组，然后<br />遍历各个单词看是否是否开头为a<br />我们现在看看怎么使用正则表达式来处理这个问题：<br /><pre name="code" class="java">
import java.util.regex.*;

public class FindA{
  public static void main(String args[])
  throws Exception{

    String candidate =
     "A Matcher examines the results of applying a pattern.";
    String regex = "\\ba\\w*\\b";
    Pattern p = Pattern.compile(regex);
    Matcher m = p.matcher(candidate);
    String val = null;
    System.out.println("INPUT: " + candidate);
    System.out.println("REGEX: " + regex +"\r\n");
    while (m.find()){
      val = m.group();
      System.out.println("MATCH: " + val);
    }
    if (val == null) {
      System.out.println("NO MATCHES: ");
    }
  }
}
</pre><br />从这个例子我们可以看到正则表达式涉及到的两个类Matcher和Pattern,我们以后会专门讨论着连个类。现在主要看看使用正则表达式的流程：<br />首先使用　Pattern的一个静态的方法compile来创建Pattern对象，<br /><pre name="code" class="java">
Pattern p = Pattern.compile(regex);
</pre><br />然后调用Pattern的方法matcher<br /><pre name="code" class="java">
 Matcher m = p.matcher(candidate);
</pre><br />得到了Matcher对象，Matcher对象保存了许多匹配信息，然后可以通过find()方法<br />查找匹配的部分，如果有匹配的部分，返回真，使用m.group方法得到匹配的各组值，<br />否则find返回false.<br />当然这只是一般的过程，还有许多更细的方法，在以后会陆续的总结，下面我们看一下<br /><pre name="code" class="java">
String regex = "\\ba\\w*\\b";
</pre><br />这个就是一个正则表达式，b,w,*都是正则表达式的meta character原字符，<br />\b表示单词的边界，w表示任意的可构成单词的字母数字，*表示前面的字母(当然可以<br />是更复杂的组之类的了东东)重复0次或0次以上，a当然还是a了。所以这个regex就<br />匹配单词开头为a的单词了。<br />二、下面总结一下基本的正则表达式的meta character以及它们含义：<br />.  匹配任意一个字符　$　匹配一行的结尾　^　匹配一行的开头(在[]里面表示否定)<br />{}　定义了一个范围　 [] 定义了一个字符类　() 定义了一个组<br /> *前面出现0次以上   +　前面匹配一次以上　?前面出现0次或一次　　<br />\　后面的字符不会看作metacharacter  \w 字母数字下划线　\W 非字母数字下划线<br />\d 单个数字　\D单个非数字　| 或，二者之一　&&与操作符 \b单词边界<br />下面看看几个简单的例子：<br />[abc] a、b 或 c（简单类） <br />[^abc] 任何字符，除了a、b 或 c（否定） <br />[a-zA-Z] a 到 z 或 A 到 Z，两头的字母包括在内（范围） <br />[a-d[m-p]] a 到 d 或 m 到 p：[a-dm-p]（并集） <br />[a-z&&[def]] d、e 或 f（交集） <br />[a-z&&[^bc]] a 到 z，除了 b 和 c：[ad-z]（减去） <br />[a-z&&[^m-p]] a 到 z，而非 m 到 p：[a-lq-z]（减去） <br />三、java.util.regex提供的操作接口：<br />java.util.regex包提供了操作正则表达式的模型，整个模型优雅而简洁，只有三个类：Pattern、Matcher和<br />PatternSyntaxException。下面将要总结他们提供的方法，以及如何灵活应用来处理文本。<br /><br />我们还是从Pattern的静态工厂方法来扩展吧：<br /><pre name="code" class="java">
static Pattern compile(String regex) 
</pre><br />将给定的正则表达式编译到模式中，并创建Pattern对象，这个方法通常是操作正则表达式的第一步，从前面那个例子<br />我们也可以看到整个的流程。<br />在看看一个重载的compile方法：<br /><pre name="code" class="java"> 
static Pattern compile(String regex, int flags) 
</pre><br />将给定的正则表达式编译到具有给定标志的模式中。 这个方法参数flags提供了一些特殊的选项来用于特殊的处理，<br />我们下面看看可使用的选项：<br />UNIX_LINES：这个主要处理UNIX和其他的操作系统在行结束符不一样的问题，UNIX使用\n代表一行的终止，而Windows<br />则使用了\r\n,\n,\r,\u2028或者\u0085作为一行的结束符。<br />CASE_INSENSITIVE：当我们在匹配的时候要忽略字符大小写时<br />COMMENTS:允许我们在正则表达式中使用注释，例如<br /><pre name="code" class="java">
Pattern p =Pattern.compile("A    #matches uppercase US-ASCII char code 65",Pattern.COMMENTS);
</pre><br />MULTILINE:表明要输入多行，他们有自己的终止字符。<br /><pre name="code" class="java">
Pattern p = Pattern.compile("^", Pattern.MULTILINE);
</pre><br />如果你的输入的字符串是：This is a sentence.\n So is this..<br />这样我们匹配的字符时This中的T和So中的S，如果不使用MULTILINE，则只会匹配T<br />DOTALL:使用这个选项之后metacharacter .就可以包括一行的终止字符了，如果没有这个选项，<br />一行的终止字符，并不会考虑在字符串之内的。<br />使用这个选项会降低效率<br /><pre name="code" class="java">
Pattern p = Pattern.compile(".", Pattern.DOTALL);
</pre><br />如果我们输入的是Test\n，则匹配的字符是5个。<br />UNICODE_CASE:处理UNICODE字符集，使用这个选项会降低效率<br />CANON_EQ：一个字符的实际存储形式是经过编码后的数字，使用CANON_EQ选项就可以匹配一个字母在各种编码了。<br />例如a可以匹配+00E0和U+0061U+0300<br />使用这个选项会降低效率<br />我们可以组合以上选项，只要使用|,进行按位或操作即可<br /><pre name="code" class="java">
Pattern p =
Pattern.compile("t # a compound flag example",Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE|
Pattern.COMMENT);
</pre><br />我们还要注意点的时Java对转译字符\的处理，例如我们要匹配一个数字：<br />我们不能使用：<br /><pre name="code" class="java">
Pattern p = Pattern.compile("\d");
</pre><br />而是：<br /><pre name="code" class="java">
Pattern p = Pattern.compile("\\d");
</pre><br />另外如果regex本身形式是错误的，compile方法会抛出java.util.regex.PatternSyntaxException异常。<br />下面我们总结一下public Matcher matcher(CharSequence input)方法：<br />当我们使用compile操作，创建了Pattern对象之后，我们就可以使用Pattern对象的matcher操作，生成<br />matcher对象了，Matcher对象包含了许多对匹配结果集的操作，我们在总结Matcher对象的时候再说。另外<br />顺便提一下参数CharSequence，CharBuffer, Segment, String, StringBuffer, StringBuilder 都实现了<br />这个接口，所以参数可以是这些中的任一种类型了。<br />下面我们看看：<br /><pre name="code" class="java">
public int flags()
</pre><br />这个方法返回了我们前面可以设置的并且已经设置的flags选项，我们通过按位与来判断是否设置了某个选项：<br /><pre name="code" class="java">
int flgs = myPattern.flags();
boolean isUsingCommentFlag =( Pattern.COMMENTS == (Pattern.COMMENTS & flgs)) ;
</pre><br />看看一个简化过程的方法：<br /><pre name="code" class="java">
public static boolean matches (String regex,CharSequence input)
</pre><br />这个方法实际上是：<br /><pre name="code" class="java">
 Pattern p = Pattern.compile(regex);
 Matcher m = p.matcher(candidate);
 m.matches()
</pre><br />过程的一个简化,我们在后面总结Matcher中的matches方法之后就会理解这个了。<br />想必我们经常使用把字符串提取出token变成字符串数组的String中的split方法吧，下面我们看看<br />类似的一个方法：<br />public String[] split(CharSequence input)<br />这个方法提供了强大的功能，因为它可以使用正则表达式来作为token的分割：<br /><pre name="code" class="java">
 Pattern p = new Pattern.compile(",|and");
 String fruits[] = p.split("apple,banana and orange");
</pre><br />split的一个重载的版本：<br /><pre name="code" class="java">
public String[] split(CharSequence input, int limit)
</pre><br />它指定了划分的组数，有以下三种情况：<br />limit==0<br />这时候和没有limit参数的那个split效果一样<br />limit>0<br />如果你仅仅对前limit个感兴趣，你可以使用limit：<br /><pre name="code" class="java">
String[] tmp = pattern.split("Hello, Dolly, You, Are, My, Favorite",3);
//tmp[0] is  "Hello",
// tmp[1] is "Dolly";
//tmp[2] is  "You, Are, My, Favorite";
</pre><br />limit&lt;0<br />会尽可能的划分所有的组，即使分割符后面是个空字符，也要单独生成一个token:""<br /><pre name="code" class="java">
Pattern p = Pattern.compile(",");
String temp[] = p.split("Hello,Dolly,", -1);
//temp[]={"Hello","Dolly",""}
</pre><br />下次再总结Matcher类，和正则表达式的一些高级部分
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/169946#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 10 Mar 2008 19:10:31 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/169946</link>
        <guid>http://fuliang.javaeye.com/blog/169946</guid>
      </item>
          <item>
        <title>面向对象的原则、模式、语言及框架（四）</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/169536" style="color:red;">http://fuliang.javaeye.com/blog/169536</a>&nbsp;
          发表时间: 2008年03月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>开-闭原则:</strong><br />任何软件在其生命周期内都会发生变化,如果我们期望开发出来的系统不会在第一版之后就被抛弃,就必须面对需求的变化而保持相对稳定.开-闭原则(The open-close principle)为我们提供了指引.<br />那什么是开-闭原则呢?<br />软件实体(模块,类,方法等)应该是可以扩展的,但是不可修改的.<br />这句话说出了软件实体应该具备的两个特征:<br />1、对扩展式开放的（Open for extension）<br />当需求变化时，我们可以对模块进行扩展，使其具有满足新需求的新行为。<br />２、对于更改时封闭的（Closed modification）<br />对模块进行扩展时，不能修改已有的源代码或二进制代码。<br />这两个方面似乎是矛盾的，我们怎么才能无需对模块进行改动的情况下改变它的功能呢？<br />关键是抽象，我们可以把一组行为抽象出一个接口/抽象类，而任意一个可能的行为则表现为<br />可能的派生类。这个原则的直接结果产生了策略模式(Strategy pattern),策略模式定义了<br />一个算法的接口，而其派生类则定义了不同的实现。当我们需要新的策略时，我们只需要重新<br />实现这个接口即可，而其他部分只依赖于这个接口，不依赖具体的实现。<br />下面我们看看一个使用策略模式满足开闭原则的例子：<br /><pre name="code" class="java">
 public interface Sort&lt;T>{
    void sort(T t[]);
 }
 public class BubbleSort&lt;T> implements BubbleSort&lt;T>{
   public void sort(T t[]){
     //the bubble sort code
   }
 }
</pre><br />我们可能需要一个效率更高的算法，而BubbleSort不能够满足我们的需求时，我们不需要修改原来的代码，直接实现一个新的算法来扩展新的功能。<br /><pre name="code" class="java">
 public class QuickSort&lt;T> implements BubbleSort&lt;T>{
   public void sort(T t[]){
     //the quick sort code
   }
 }
</pre><br />而其他的代码则只依赖于我们的Sort接口：<br /><pre name="code" class="java">
 class SomeClassUseSort{
   private Sort sort;
   //other codes
 }
</pre><br />下面我们看看Bob大叔举的一个违反OCP的例子：<br />绘制图形的例子：<br /><pre name="code" class="java">
class Shape{}
class Circle extends Shape{}
class Square extends Shape{}

class DrawAll{
 public void drawAll(List&lt;Shape> shapes){  
   for(Shape shape : shapes){
      if(shape instanceof Circle)
          drawCircle();
       else if(shape instanceof Square){
          drawSquare();
       }    
   }
 }
 private void drawCircle(){
   //draw circle;
 }
 private void drawSquare(){
   //draw square
 }
}
</pre><br />当我们需要绘制三角形时，我们需要在ShapeDraw中添加drawTriangle方法<br />并且需要修改drawAll方法，以便能够绘制三角形。<br />这个例子违反了开闭原则，我们下面考虑如何重构这个例子：<br /><pre name="code" class="java">
class Shape{
 public abstract void draw();
}
class Circle extends Shape{
  public void draw(){
   //draw circle
  }
}
class Square extends Shape{
  public void draw(){
    //draw Shape
  }
}
class DrawAll{
  public void drawAll(List&lt;Shape> shapes){  
   for(Shape shape : shapes){
      shape.draw();
   }
  }
}
</pre><br />当我们怎加新的图形时，我们只需要继承Shape并实现自己的draw方法即可，而无需<br />修改DrawAll类。这就是遵循开闭原则的威力。其实大部分违反开闭原则都是过程化<br />的思想所致，当你发现你的类中充斥着一连串的if语句时，基本上说它违反了开闭原则。<br />现在我们的代码真的满足开闭原则了么？<br />那我们再看看吧，新的需求来了，要求我们按照形状的顺序来绘制图形，我们现在的代码<br />如果不作修改的话显得无能为力了。<br />好的，那我们再抽象出DrawAll接口：<br /><pre name="code" class="java">
interface DrawAll{
  public abstract void drawAll(List&lt;Shape> shapes);
}

class DrawAllByListIndex　implements DrawAll{
  public void drawAll(List&lt;Shape> shapes){  
   for(Shape shape : shapes){
      shape.draw();
   }
  }
}
</pre><br />这样我们可以<br /><pre name="code" class="java">
class DrawAllByShape implements DrawAll{
   public void drawAll(List&lt;Shape> shapes){ 
   sortByShape(shapes); 
   for(Shape shape : shapes){
      shape.draw();
   }
  }
}
</pre><br />来扩展DrawAll就ok了。<br />就像我们开始抽象的那样，我们根本没有预测到按照图形顺序绘制的需求，我们也不可能<br />每个东西都抽象出一个接口，因为这样会产生不必要的复杂性，有点过度设计的味道。所以<br />我们能做的只是去刺激需求，尽快地了解需求可能的变化，我们可以采用测试驱动的方法，首先构建一个可测试的抽象，并且通常这个可测试的抽象会隔离以后可能要发生的其他种变化。<br /><strong>结论</strong><br />OCP可以说是面向对象设计的核心，遵循这个原则，可以使系统具有更好的灵活性、重用性和可维护性。但是如果肆无忌惮的使用这个原则，进行过度的抽象同样不是好的做法。正确的做法是仅对频繁发生变化的地方进行抽象，拒绝不成熟的抽象和抽象本身同样重要。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/169536#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 09 Mar 2008 17:17:32 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/169536</link>
        <guid>http://fuliang.javaeye.com/blog/169536</guid>
      </item>
          <item>
        <title>面向对象的原则、模式、语言及框架（三）</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/169387" style="color:red;">http://fuliang.javaeye.com/blog/169387</a>&nbsp;
          发表时间: 2008年03月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>单一职责原则 </strong><br />这个原则描述了内聚性：一个模块组成元素的功能相关性。单一职责原则描述了引起类变化的原则只有一个，而职责就是变化的原因，如果你能够想到多于一个动机去改变类，那么这个类就具有多于一个职责。<br />例如<br /><pre name="code" class="java">
class Rectangle{
  public void draw();
  public double area();
}
</pre><br />如果这个类为两个不同的应用程序所使用：计算几何学方面的，它只需要提供计算面积的方法，图形绘制方面的，它可能只需要在屏幕上绘制自己。那么这个类就有了两个职责，这种设计违反了单一职责原则。当我们在计算几何学方面方面需要新的功能时我们需要修改这个类，<br />当在图形绘制方面修改这个类时也需要修改这个类。<br />这样我们可以把这个Rectangle分成两个：<br /><pre name="code" class="java">
class GeometricRantangle{
  public double area();
}
class GUIRectangle(){
  public void draw();
}
</pre><br />这样当我们需要在计算几何学方面使用Rantangle时，我们直接使用GeometricRantangle，图形绘制方面直接使用GUIRectangle。<br />再看一个调制解调器的例子：<br /><pre name="code" class="java">
interface Moderm{
  public void dial(String pno);
  public void hangup();
  public void send(char c);
  public void recv();
}
</pre><br />多数人会认为这个接口是合理的，但这个接口却承担了两个职责：一是连接管理；二是数据通信。而在大多数应用中，连接管理和数据通信往往是分开的。如果无论连接管理发生了变化还是数据通信发生了变化，我们都需要修改这个类，这就造成了编译和部署的次数会超过我们希望的次数，这个设计具有僵化的Bad Smell.这种情况下我们希望把这两个职责分离：<br /><pre name="code" class="java">
interface Connection{
 public void dial(String pno);
 public void hangup();
}

interface Channel{
  public void send(char c);
  public void recv();
}
class Moderm implements Connection,Channel{
  //...
}
</pre><br />虽然我们看到Moderm依然是一个杂凑物，似乎也有两个职责，但这是必须的，因为这是对Moderm 功能真实的抽象。但是，这样我们需要连接管理的时候，我们只依赖于Connection接口，当我们需要数据通信的时候只需要依赖Channel接口，谁都不需要依赖具体的Moderm.单一职责通常是针对提供给用户接口来说的，而实际类作为一个实际的对用物，有<br />多种职责可能是必须的，只要我们抽象出单一职责的接口，让其他的代码只依赖于这些接口就行了。<br />我们在企业级开发时经常遇到的情况是，在业务层写持久化规则，业务规则是经常变化的，而持久化规则通常是稳定的。如果不把他们分离，每次变化我们都要编译部署整个混合体，这只能是自找苦吃。通常的做法是使用DAO模式，分离出数据访问层，让业务层依赖于DAO接口，这样业务层发生变化时，不会影响 数据访问层，并且可以随时替换掉数据访问层的具体实现。<br />当然如果应用程序的变化总会导致这两种职责的同时变化，那么就不必分离他们，毕竟单一职责原则会带来一些复杂性，生成更多的接口。如果过度使用这个原则会造成过度设计的Bad Smell.
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/blog/169387#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/70' target='_blank'><span style="color:red;font-weight:bold;">第二届网络工程师侠客行大会5月24日杭州举行</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 08 Mar 2008 18:45:22 +0800</pubDate>
        <link>http://fuliang.javaeye.com/blog/169387</link>
        <guid>http://fuliang.javaeye.com/blog/169387</guid>
      </item>
          <item>
        <title>面向对象的原则、模式、语言及框架（二）</title>
        <author>fuliang</author>
        <description>
          <![CDATA[
          <br/>
          网站: <a href="http://www.javaeye.com">JavaEye</a>&nbsp;
          作者: <a href="http://fuliang.javaeye.com">fuliang</a>&nbsp;
                    链接：<a href="http://fuliang.javaeye.com/blog/169379" style="color:red;">http://fuliang.javaeye.com/blog/169379</a>&nbsp;
          发表时间: 2008年03月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>一、一些拙劣的设计症结</strong><br />说起面向对象的原则，不得不提一些拙劣的一些设计症结，和代码的Bad Smell类似，但他们处于更高的层次，是整个软件结构的Bad Smell.<br />这些症结主要有：<br /><strong>僵化性（Rigdity）：</strong>设计难于改变。<br />很难对系统进行改动，因为每个改动会迫使许多对系统的其他部分做改动，这往往是系统耦合性太高.<br /><strong>脆弱性(Fragility):</strong>设计易于遭到破坏。<br />在一次改动时,程序的许多地方都会出问题,而且常常出新问题的地方和改动的地方并没有概念上的联系.这些模块不断的修改,然而你越是修改他们,他们就变的更糟.<br /><strong>牢固性（Immobility）:</strong>设计难于重用。<br />设计中包含了许多对其他系统有用的部分,但是要把这部分从系统中分离出来所需要的努力和风险是巨大的.<br /><strong>粘滞性（Viscosity）:</strong>难以做正确的事情。<br />主要表现在软件的粘滞性和环境的粘滞性。当面临一个改动是，开发人员会有许多的改动方法，有的方法能够保持设计，有的却破坏设计，但是保持设计的方法变得越发难以应用，这就<br />表明设计具有很高的粘滞性。当开发环境迟钝、低效时，例如编译代码花费的时间很长，开发人员往往被诱使做不会导致大规模重新编译的事情，即时这些改动会破坏设计，这就是环境粘滞。<br /><strong>不必要复杂性（Needless Complexity）:</strong>过分设计。<br />设计包含当前没有用到的部分，开发人员预测需求的可能变化，并放置了处理那些潜在变化的代码，为过多的可能性做准备，导致了绝不会用到的结构。<br /><strong>不必要的重复性（Needless Repetition）:</strong>滥用ctr+c,ctrl+v<br />复制粘贴，许多开发人员惯用的伎俩。当他们需要写一个功能时，在原有的代码中，查找是否有类似的功能，找到后直接拷贝过来，稍加修改便ok了。似乎是个很高效的方法，然而这样导致的结果是缺少必要的抽象，当在一个重复的代码中发现错误或者增加功能时，需要同时更新其他的重复代码。<br /><strong>晦涩性（Opacity）:</strong>混乱的表达。<br />是指模块难以理解，代码需要清晰和富有活力的表达，然而也可能一种晦涩的方式表达，并且当代码随着时间演化时会变得越来越晦涩。这就要求代码的编写者要站在阅读者的角度写代码，写出清晰易于理解的代码。<br />二、面向对象的设计原则：<br />单一职责原则(The single responsibility principle)SRP<br />开-闭原则(the open-close principle) OCP<br />liskov替换原则(the liskov subsititution principle)LSP<br />依赖倒置原则(The dependency inversion principle)DIP<br />接口分离原则(The interface segregation interface)ISP<br />组合/聚合复用原则(Composition/Aggregation Principle)CARP <br />迪米特法则 (Law of Demeter)LoD <br />这些原则是数十年软件工程来之不易的成果，使许多软件开发人员和研究人员思想和著作的结晶。设计中的Bad smell,往往是违反了这几种原则的一种或几种而导致的结果。设计模式都是遵循了这些原则，在特定环境下一类问题的解决方案。分析设计模式从设计原则上分析才能知道为什么要这么设计。后面将要总结这些原则是如何去除设计中的Bad Smell的。当然过度使用这些原则会导致过度设计的Bad Smell.<br />参考：Bob大叔的《敏捷软件开发：原则、模式与实践》
          <br/>
     