<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>JavaEye博客</title>
    <description>Java博客,Ruby on Rails博客,AJAX博客,Agile博客 -- JavaEye做最棒的软件开发交流社区</description>
    <link>http://www.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>奥运开幕式如何</title>
        <author>happycookie</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://happycookie.javaeye.com">happycookie</a>&nbsp;
          链接：<a href="http://happycookie.javaeye.com/blog/225983" style="color:red;">http://happycookie.javaeye.com/blog/225983</a>&nbsp;
          发表时间: 2008年08月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>在国内各大知名网站都在抢先报导开幕式的盛况和我们大家彼此谈论和分享看点时,知道各大国际网站对此的报导吗?</p>
<p>&nbsp;</p>
<p>去BBC、泰晤士报、New York Times、economist、CNN等逛了逛,心情突然没那么好了.</p>
<p>&nbsp;</p>
<p>在他们的网站上,盛况只是了了带过,他们更关注的问题(也是主体报导)却是其他问题:</p>
<p>&nbsp;</p>
<p>首当其冲的是安全(security)问题,他们记者报导是北京的气氛比较紧张,各种小事件又更加加重了这种气氛,比如当天抓了三名抗议、支持藏独
的美国人,还有日本飞机返回东京(说接到的邮件中声称到北京上空会遭到爆炸),加之前面藏独闹事和新疆的各种恐怖活动.也难怪他们这样想!在他们的报导
中,也可以看出政府压力真的很大(为了安全问题,北京天安门附近戒严的很厉害,各种安检也更严格,生怕出事).不管怎样,希望这个奥运会能圆满成功吧,在
这不平凡的一年里,中国人民确实需要这次成功! 闹事分子,去你的吧!</p>
<p>&nbsp;</p>
<p>其次问题就是空气污染问题了,按理来说北京空气应该不错啊,官方有人解释是天气问题,不过他们总认为北京空气向来如此,这也难怪前两天美国自行车运动员进机场时带着口罩了.难道国外空气质量真的特别好吗,以至于他们说这样的空气会威胁他们的健康(汗一个).</p>
<p>&nbsp;</p>
<p>然后就是人权问题,这个东西,不好评论.</p>
<p>&nbsp;</p>
<p>其他问题就是开幕式现场问题了,烟花放的过了(呛鼻子)、中国人好客(对和自己友好的国家热情,对反对自己的国家冷淡).虽然有些鸡蛋里挑骨头了,但也是一些我们没想到的问题.</p>
<p>&nbsp;</p>
<p>看了这么多报导,到底是他们因为中国威胁论而不够宽容还是我们需要更加努力呢?</p>
<p>&nbsp;</p>
<p>而无论如何,我相信更多的人是在欣赏这次盛会.我也更期待这次奥运会的圆满成功并让世人见证.</p>
          <br/>
          <span style="color:red;">
            <a href="http://happycookie.javaeye.com/blog/225983#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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, 09 Aug 2008 03:10:38 +0800</pubDate>
        <link>http://happycookie.javaeye.com/blog/225983</link>
        <guid>http://happycookie.javaeye.com/blog/225983</guid>
      </item>
      <item>
        <title>java反射机制</title>
        <author>whaosoft</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://whaosoft.javaeye.com">whaosoft</a>&nbsp;
          链接：<a href="http://whaosoft.javaeye.com/blog/225979" style="color:red;">http://whaosoft.javaeye.com/blog/225979</a>&nbsp;
          发表时间: 2008年08月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          whaosoft<br />http://hsoft.vicp.cc<br /><img src="/images/smiles/icon_idea.gif"/><br />Java提供反射机制来动态执行方法和构造方法，以及数组操作等。<br />1. 得到某个对象的属性<br /><br />1 public Object getProperty(Object owner, String fieldName) throws Exception {<br />2     Class ownerClass = owner.getClass();<br />3 <br />4     Field field = ownerClass.getField(fieldName);<br />5 <br />6     Object property = field.get(owner);<br />7 <br />8     return property;<br />9 }<br />Class ownerClass = owner.getClass()：得到该对象的Class。<br /><br />Field field = ownerClass.getField(fieldName)：通过Class得到类声明的属性。<br /><br />Object property = field.get(owner)：通过对象得到该属性的实例，如果这个属性是非公有的，这里会报IllegalAccessException。<br /><br /><br /><br />2. 得到某个类的静态属性<br /><br /> 1 public Object getStaticProperty(String className, String fieldName)<br /> 2             throws Exception {<br /> 3     Class ownerClass = Class.forName(className);<br /> 4 <br /> 5     Field field = ownerClass.getField(fieldName);<br /> 6 <br /> 7     Object property = field.get(ownerClass);<br /> 8 <br /> 9     return property;<br />10 }<br /><br />Class ownerClass = Class.forName(className) ：首先得到这个类的Class。<br /><br />Field field = ownerClass.getField(fieldName)：和上面一样，通过Class得到类声明的属性。<br /><br />Object property = field.get(ownerClass) ：这里和上面有些不同，因为该属性是静态的，所以直接从类的Class里取。<br /><br /><br />3. 执行某对象的方法<br /><br /> 1 public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {<br /> 2 <br /> 3     Class ownerClass = owner.getClass();<br /> 4 <br /> 5     Class[] argsClass = new Class[args.length];<br /> 6 <br /> 7     for (int i = 0, j = args.length; i &lt; j; i++) {<br /> 8         argsClass[i] = args[i].getClass();<br /> 9     }<br />10 <br />11     Method method = ownerClass.getMethod(methodName, argsClass);<br />12 <br />13     return method.invoke(owner, args);<br />14 }<br />Class owner_class = owner.getClass() ：首先还是必须得到这个对象的Class。<br /><br />5～9行：配置参数的Class数组，作为寻找Method的条件。<br /><br />Method method = ownerClass.getMethod(methodName, argsClass)：通过Method名和参数的Class数组得到要执行的Method。<br /><br />method.invoke(owner, args)：执行该Method，invoke方法的参数是执行这个方法的对象，和参数数组。返回值是Object，也既是该方法的返回值。<br /><br /><br />4. 执行某个类的静态方法<br /><br /> 1 public Object invokeStaticMethod(String className, String methodName,<br /> 2             Object[] args) throws Exception {<br /> 3     Class ownerClass = Class.forName(className);<br /> 4 <br /> 5     Class[] argsClass = new Class[args.length];<br /> 6 <br /> 7     for (int i = 0, j = args.length; i &lt; j; i++) {<br /> 8         argsClass[i] = args[i].getClass();<br /> 9     }<br />10 <br />11     Method method = ownerClass.getMethod(methodName, argsClass);<br />12 <br />13     return method.invoke(null, args);<br />14 }<br /><br />基本的原理和实例3相同，不同点是最后一行，invoke的一个参数是null，因为这是静态方法，不需要借助实例运行。<br /><br /><br /><br />5. 新建实例<br /> 1 <br /> 2 public Object newInstance(String className, Object[] args) throws Exception {<br /> 3     Class newoneClass = Class.forName(className);<br /> 4 <br /> 5     Class[] argsClass = new Class[args.length];<br /> 6 <br /> 7     for (int i = 0, j = args.length; i &lt; j; i++) {<br /> 8         argsClass[i] = args[i].getClass();<br /> 9     }<br />10 <br />11     Constructor cons = newoneClass.getConstructor(argsClass);<br />12 <br />13     return cons.newInstance(args);<br />14 <br />15 }<br /><br />这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数，可以直接使用newoneClass.newInstance()来实现。<br /><br />Class newoneClass = Class.forName(className)：第一步，得到要构造的实例的Class。<br /><br />第5～第9行：得到参数的Class数组。<br /><br />Constructor cons = newoneClass.getConstructor(argsClass)：得到构造子。<br /><br />cons.newInstance(args)：新建实例。<br /><br /><br />6. 判断是否为某个类的实例<br /><br />1 public boolean isInstance(Object obj, Class cls) {<br />2     return cls.isInstance(obj);<br />3 }<br /><br /><br />7. 得到数组中的某个元素<br />1 public Object getByArray(Object array, int index) {<br />2     return Array.get(array,index);<br />3 }<br /><br /><br />附完整源码：<br /><br />import java.lang.reflect.Array;<br />import java.lang.reflect.Constructor;<br />import java.lang.reflect.Field;<br />import java.lang.reflect.Method;<br /><br /><br />/**<br /> * Java Reflection Cookbook<br /> *<br /> * @author Michael Lee<br /> * @since 2006-8-23<br /> * @version 0.1a<br /> */<br /><br />public class Reflection {<br />    /**<br />     * 得到某个对象的公共属性<br />     *<br />     * @param owner, fieldName<br />     * @return 该属性对象<br />     * @throws Exception<br />     *<br />     */<br />    public Object getProperty(Object owner, String fieldName) throws Exception {<br />        Class ownerClass = owner.getClass();<br /><br />        Field field = ownerClass.getField(fieldName);<br /><br />        Object property = field.get(owner);<br /><br />        return property;<br />    }<br /><br />    /**<br />     * 得到某类的静态公共属性<br />     *<br />     * @param className   类名<br />     * @param fieldName   属性名<br />     * @return 该属性对象<br />     * @throws Exception<br />     */<br />    public Object getStaticProperty(String className, String fieldName)<br />            throws Exception {<br />        Class ownerClass = Class.forName(className);<br /><br />        Field field = ownerClass.getField(fieldName);<br /><br />        Object property = field.get(ownerClass);<br /><br />        return property;<br />    }<br /><br /><br />    /**<br />     * 执行某对象方法<br />     *<br />     * @param owner<br />     *            对象<br />     * @param methodName<br />     *            方法名<br />     * @param args<br />     *            参数<br />     * @return 方法返回值<br />     * @throws Exception<br />     */<br />    public Object invokeMethod(Object owner, String methodName, Object[] args)<br />            throws Exception {<br /><br />        Class ownerClass = owner.getClass();<br /><br />        Class[] argsClass = new Class[args.length];<br /><br />        for (int i = 0, j = args.length; i &lt; j; i++) {<br />            argsClass[i] = args[i].getClass();<br />        }<br /><br />        Method method = ownerClass.getMethod(methodName, argsClass);<br /><br />        return method.invoke(owner, args);<br />    }<br /><br /><br />      /**<br />     * 执行某类的静态方法<br />     *<br />     * @param className<br />     *            类名<br />     * @param methodName<br />     *            方法名<br />     * @param args<br />     *            参数数组<br />     * @return 执行方法返回的结果<br />     * @throws Exception<br />     */<br />    public Object invokeStaticMethod(String className, String methodName,<br />            Object[] args) throws Exception {<br />        Class ownerClass = Class.forName(className);<br /><br />        Class[] argsClass = new Class[args.length];<br /><br />        for (int i = 0, j = args.length; i &lt; j; i++) {<br />            argsClass[i] = args[i].getClass();<br />        }<br /><br />        Method method = ownerClass.getMethod(methodName, argsClass);<br /><br />        return method.invoke(null, args);<br />    }<br /><br /><br /><br />    /**<br />     * 新建实例<br />     *<br />     * @param className<br />     *            类名<br />     * @param args<br />     *            构造函数的参数<br />     * @return 新建的实例<br />     * @throws Exception<br />     */<br />    public Object newInstance(String className, Object[] args) throws Exception {<br />        Class newoneClass = Class.forName(className);<br /><br />        Class[] argsClass = new Class[args.length];<br /><br />        for (int i = 0, j = args.length; i &lt; j; i++) {<br />            argsClass[i] = args[i].getClass();<br />        }<br /><br />        Constructor cons = newoneClass.getConstructor(argsClass);<br /><br />        return cons.newInstance(args);<br /><br />    }<br /><br /><br />    <br />    /**<br />     * 是不是某个类的实例<br />     * @param obj 实例<br />     * @param cls 类<br />     * @return 如果 obj 是此类的实例，则返回 true<br />     */<br />    public boolean isInstance(Object obj, Class cls) {<br />        return cls.isInstance(obj);<br />    }<br />    <br />    /**<br />     * 得到数组中的某个元素<br />     * @param array 数组<br />     * @param index 索引<br />     * @return 返回指定数组对象中索引组件的值<br />     */<br />    public Object getByArray(Object array, int index) {<br />        return Array.get(array,index);<br />    }<br />}
          <br/>
          <span style="color:red;">
            <a href="http://whaosoft.javaeye.com/blog/225979#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 09 Aug 2008 01:14:32 +0800</pubDate>
        <link>http://whaosoft.javaeye.com/blog/225979</link>
        <guid>http://whaosoft.javaeye.com/blog/225979</guid>
      </item>
      <item>
        <title>千呼万唤的奥运会主题曲太让人失望了</title>
        <author>tryonmind</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tryonmind.javaeye.com">tryonmind</a>&nbsp;
          链接：<a href="http://tryonmind.javaeye.com/blog/225975" style="color:red;">http://tryonmind.javaeye.com/blog/225975</a>&nbsp;
          发表时间: 2008年08月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 终于等到了这一天，终于数着各个不认识的国家的名字等到了奥运会的主题曲。不过却没有激情，没有惊喜，只有失望。主题曲是《我和你》？太平淡的一首歌了，和《亚洲雄风》比起来就不是一个级别的，都不能和《北京欢迎你》比，《北京欢迎你》多朗朗上口啊，旋律歌词都很棒！甚至都不如《拥抱爱的梦想》！就连《点燃激情，传递梦想》、《we are ready》都比这首歌好！</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 历经几届选曲，最终选出这样一首歌，个人觉得有点有失水准！再加上歌手在唱歌的时候还有解说员的解说，烟火声，更是淡化了歌声，失望了。<img src="../../images/smiles/icon_cry.gif" alt="" />&nbsp; 引用网友一句话：&ldquo;谁脑壳有包，选了这么首烂歌，肯定，本届主题歌将是开幕式最大败笔&nbsp;&rdquo;。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 不过这个开幕式换算不错，只是有些地方觉得有点罗嗦。 展现四大发明的那幅画卷和最后的点火都挺好！</p>
          <br/>
          <span style="color:red;">
            <a href="http://tryonmind.javaeye.com/blog/225975#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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, 09 Aug 2008 00:52:05 +0800</pubDate>
        <link>http://tryonmind.javaeye.com/blog/225975</link>
        <guid>http://tryonmind.javaeye.com/blog/225975</guid>
      </item>
      <item>
        <title>出院了，我的感受颇多</title>
        <author>gotothework</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://gotothework.javaeye.com">gotothework</a>&nbsp;
          链接：<a href="http://gotothework.javaeye.com/blog/225967" style="color:red;">http://gotothework.javaeye.com/blog/225967</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          今天打完最后一针消炎针，我就可以出院了，虽然仅仅住院不到一周的时间，但是在这不到一周的时间里，使我看到了太多，感受到了太多......<br /><br />1号床的老爷子，家在吉林，靠种地维持生活，这次动手术，花了40000多，家里一年的活都等于白干了。在医院，他儿子看护，如果晚上想在没人的病床上住，必须交10元的租床费，为了省下10块钱，天天在走廊打地铺。<br /><br />2号床老爷子，家是本市的，得了肺癌，不过是初期，可以抑制住，天天白天来打化疗针，晚上回家住，姑娘，儿子总来看，总是愁眉苦脸的，怕父亲有什么事，可是老爷子总是笑呵的告诉他们没事，别担心。<br /><br />5号床老爷子(医院没有4号，不吉利)，可以说，全身很多病，眼睛，耳朵，肺，都有毛病，如果治疗的化需要花很多钱，他儿子争气，在国外搞汽车设计的，老爷子的病也顺顺利利一个个治好。<br /><br />不仅是这3个老爷子，其他床的病人，护士都给了我太多的启示，太多的感受......<br /><br />不要不在乎那区区10块钱，有人为了省下这10块钱而在走廊打地铺。<br /><br />得病不可怕，怕的是不敢去面对自己的病，要勇敢的面对，这才是战胜病魔的第一步。<br /><br />做儿女的不要贪图玩乐，一定要努力工作，学习，当父母老迈的时候，让父母享受人生。<br /><br />在我住的隔壁房间，有3个病人是因为喝酒过量而引起的食道病，尽量少喝酒，特别是白酒。<br /><br />少吃麻辣烫，米线这一类的食物，里面致癌物很多，我已经有一个同学因为总吃麻辣烫得淋巴癌了，而且，这些东西及其不卫生，到了人体里会促使微生物的生长。<br /><br />少喝滚烫的液体，如茶，开水之类的，再医院听人说，总喝滚烫的东西很容易把嗓子和食道烫坏，最后导致其他病变。<br /><br />时不时就去做个身体检查，很多病是不声不响的来到你身边。<br /><br />在医院打点滴，一定别让实习护士给你扎，本人住院5天，共打10针，全部由实习护士主扎，扎滚针3次，扎里不回血4次，一次成功仅3次，目前双手肿痛，打字困难。<br /><br />最后，只要人活着，就什么都会有，人没了，那真就什么都没了，珍惜自己的身体，没事去趟医院检查下，这不仅是对你自己负责，也是对关心你的人负责。
          <br/>
          <span style="color:red;">
            <a href="http://gotothework.javaeye.com/blog/225967#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Aug 2008 23:04:58 +0800</pubDate>
        <link>http://gotothework.javaeye.com/blog/225967</link>
        <guid>http://gotothework.javaeye.com/blog/225967</guid>
      </item>
      <item>
        <title>JAVA面试题解惑系列（十）——话说多线程</title>
        <author>臧圩人</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://zangweiren.javaeye.com">臧圩人</a>&nbsp;
          链接：<a href="http://zangweiren.javaeye.com/blog/225949" style="color:red;">http://zangweiren.javaeye.com/blog/225949</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          作者：臧圩人（zangweiren）<br />网址：http://zangweiren.javaeye.com<br /><br />>>><span style="color: red"><strong>转载请注明出处！</strong></span>&lt;&lt;&lt;<br /><br />线程或者说多线程，是我们处理多任务的强大工具。线程和进程是不同的，每个进程都是一个独立运行的程序，拥有自己的变量，且不同进程间的变量不能共享；而线程是运行在进程内部的，每个正在运行的进程至少有一个线程，而且不同的线程之间可以在进程范围内共享数据。也就是说进程有自己独立的存储空间，而线程是和它所属的进程内的其他线程共享一个存储空间。线程的使用可以使我们能够并行地处理一些事情。线程通过并行的处理给用户带来更好的使用体验，比如你使用的邮件系统（outlook、Thunderbird、foxmail等），你当然不希望它们在收取新邮件的时候，导致你连已经收下来的邮件都无法阅读，而只能等待收取邮件操作执行完毕。这正是线程的意义所在。<br /><br /><em><strong><span style="font-size: medium">实现线程的方式</span></strong></em><br /><br />实现线程的方式有两种：<br /><ol><li>继承java.lang.Thread，并重写它的run()方法，将线程的执行主体放入其中。</li><li>实现java.lang.Runnable接口，实现它的run()方法，并将线程的执行主体放入其中。</li></ol><br />这是继承Thread类实现线程的示例：<br /><pre name="code" class="java">
public class ThreadTest extends Thread {
	public void run() {
		// 在这里编写线程执行的主体
		// do something
	}
}
</pre><br />这是实现Runnable接口实现多线程的示例：<br /><pre name="code" class="java">
public class RunnableTest implements Runnable {
	public void run() {
		// 在这里编写线程执行的主体
		// do something
	}
}
</pre><br />这两种实现方式的区别并不大。继承Thread类的方式实现起来较为简单，但是继承它的类就不能再继承别的类了，因此也就不能继承别的类的有用的方法了。而使用是想Runnable接口的方式就不存在这个问题了，而且这种实现方式将线程主体和线程对象本身分离开来，逻辑上也较为清晰，所以推荐大家更多地采用这种方式。<br /><br /><em><strong><span style="font-size: medium">如何启动线程</span></strong></em><br /><br />我们通过以上两种方式实现了一个线程之后，线程的实例并没有被创建，因此它们也并没有被运行。我们要启动一个线程，必须调用方法来启动它，这个方法就是Thread类的start()方法，而不是run()方法（既不是我们继承Thread类重写的run()方法，也不是实现Runnable接口的run()方法）。run()方法中包含的是线程的主体，也就是这个线程被启动后将要运行的代码，它跟线程的启动没有任何关系。上面两种实现线程的方式在启动时会有所不同。<br /><br />继承Thread类的启动方式：<br /><pre name="code" class="java">
public class ThreadStartTest {
	public static void main(String[] args) {
		// 创建一个线程实例
		ThreadTest tt = new ThreadTest();
		// 启动线程
		tt.start();
	}
}
</pre><br />实现Runnable接口的启动方式：<br /><pre name="code" class="java">
public class RunnableStartTest {
	public static void main(String[] args) {
		// 创建一个线程实例
		Thread t = new Thread(new RunnableTest());
		// 启动线程
		t.start();
	}
}
</pre><br />实际上这两种启动线程的方式原理是一样的。首先都是调用本地方法启动一个线程，其次是在这个线程里执行目标对象的run()方法。那么这个目标对象是什么呢？为了弄明白这个问题，我们来看看Thread类的run()方法的实现：<br /><pre name="code" class="java">
public void run() {
	if (target != null) {
		target.run();
	}
}
</pre><br />当我们采用实现Runnable接口的方式来实现线程的情况下，在调用new Thread(Runnable target)构造器时，将实现Runnable接口的类的实例设置成了线程要执行的主体所属的目标对象target，当线程启动时，这个实例的run()方法就被执行了。当我们采用继承Thread的方式实现线程时，线程的这个run()方法被重写了，所以当线程启动时，执行的是这个对象自身的run()方法。总结起来就一句话，线程类有一个Runnable类型的target属性，它是线程启动后要执行的run()方法所属的主体，如果我们采用的是继承Thread类的方式，那么这个target就是线程对象自身，如果我们采用的是实现Runnable接口的方式，那么这个target就是实现了Runnable接口的类的实例。<br /><br /><em><strong><span style="font-size: medium">线程的状态</span></strong></em><br /><br />在Java 1.4及以下的版本中，每个线程都具有新建、可运行、阻塞、死亡四种状态，但是在Java 5.0及以上版本中，线程的状态被扩充为新建、可运行、阻塞、等待、定时等待、死亡六种。线程的状态完全包含了一个线程从新建到运行，最后到结束的整个生命周期。线程状态的具体信息如下：<br /><ol><li><strong>NEW（新建状态、初始化状态）：</strong>线程对象已经被创建，但是还没有被启动时的状态。这段时间就是在我们调用new命令之后，调用start()方法之前。</li><li><strong>RUNNABLE（可运行状态、就绪状态）：</strong>在我们调用了线程的start()方法之后线程所处的状态。处于RUNNABLE状态的线程在JAVA虚拟机（JVM）上是运行着的，但是它可能还正在等待操作系统分配给它相应的运行资源以得以运行。</li><li><strong>BLOCKED（阻塞状态、被中断运行）：</strong>线程正在等待其它的线程释放同步锁，以进入一个同步块或者同步方法继续运行；或者它已经进入了某个同步块或同步方法，在运行的过程中它调用了某个对象继承自java.lang.Object的wait()方法，正在等待重新返回这个同步块或同步方法。</li><li><strong>WAITING（等待状态）：</strong>当前线程调用了java.lang.Object.wait()、java.lang.Thread.join()或者java.util.concurrent.locks.LockSupport.park()三个中的任意一个方法，正在等待另外一个线程执行某个操作。比如一个线程调用了某个对象的wait()方法，正在等待其它线程调用这个对象的notify()或者notifyAll()（这两个方法同样是继承自Object类）方法来唤醒它；或者一个线程调用了另一个线程的join()（这个方法属于Thread类）方法，正在等待这个方法运行结束。</li><li><strong>TIMED_WAITING（定时等待状态）：</strong>当前线程调用了java.lang.Object.wait(long timeout)、java.lang.Thread.join(long millis)、java.util.concurrent.locks.LockSupport.packNanos(long nanos)、java.util.concurrent.locks.LockSupport.packUntil(long deadline)四个方法中的任意一个，进入等待状态，但是与WAITING状态不同的是，它有一个最大等待时间，即使等待的条件仍然没有满足，只要到了这个时间它就会自动醒来。</li><li><strong>TERMINATED（死亡状态、终止状态）：</strong>线程完成执行后的状态。线程执行完run()方法中的全部代码，从该方法中退出，进入TERMINATED状态。还有一种情况是run()在运行过程中抛出了一个异常，而这个异常没有被程序捕获，导致这个线程异常终止进入TERMINATED状态。</li></ol><br />在Java5.0及以上版本中，线程的全部六种状态都以枚举类型的形式定义在java.lang.Thread类中了，代码如下：<br /><pre name="code" class="java">
public enum State {
	NEW,
	RUNNABLE,
	BLOCKED,
	WAITING,
	TIMED_WAITING,
	TERMINATED;
}
</pre><br /><em><strong><span style="font-size: medium">sleep()和wait()的区别</span></strong></em><br /><br />sleep()方法和wait()方法都成产生让当前运行的线程停止运行的效果，这是它们的共同点。下面我们来详细说说它们的不同之处。<br /><br />sleep()方法是本地方法，属于Thread类，它有两种定义：<br /><pre name="code" class="java">
public static native void sleep(long millis) throws InterruptedException;

public static void sleep(long millis, int nanos) throws InterruptedException {
	//other code
}
</pre><br />其中的参数millis代表毫秒数（千分之一秒），nanos代表纳秒数（十亿分之一秒）。这两个方法都可以让调用它的线程沉睡（停止运行）指定的时间，到了这个时间，线程就会自动醒来，变为可运行状态（RUNNABLE），但这并不表示它马上就会被运行，因为线程调度机制恢复线程的运行也需要时间。调用sleep()方法并不会让线程释放它所持有的同步锁；而且在这期间它也不会阻碍其它线程的运行。上面的连个方法都声明抛出一个InterruptedException类型的异常，这是因为线程在sleep()期间，有可能被持有它的引用的其它线程调用它的interrupt()方法而中断。中断一个线程会导致一个InterruptedException异常的产生，如果你的程序不捕获这个异常，线程就会异常终止，进入TERMINATED状态，如果你的程序捕获了这个异常，那么程序就会继续执行catch语句块（可能还有finally语句块）以及以后的代码。<br /><br />为了更好地理解interrupt()效果，我们来看一下下面这个例子：<br /><pre name="code" class="java">
public class InterruptTest {
	public static void main(String[] args) {
		Thread t = new Thread() {
			public void run() {
				try {
					System.out.println("我被执行了-在sleep()方法前");
					// 停止运行10分钟
					Thread.sleep(1000 * 60 * 60 * 10);
					System.out.println("我被执行了-在sleep()方法后");
				} catch (InterruptedException e) {
					System.out.println("我被执行了-在catch语句块中");
				}
				System.out.println("我被执行了-在try{}语句块后");
			}
		};
		// 启动线程
		t.start();
		// 在sleep()结束前中断它
		t.interrupt();
	}
}
</pre><br />运行结果：<br /><ol><li>我被执行了-在sleep()方法前</li><li>我被执行了-在catch语句块中</li><li>我被执行了-在try{}语句块后</li></ol><br />wait()方法也是本地方法，属于Object类，有三个定义：<br /><pre name="code" class="java">
public final void wait() throws InterruptedException {
	//do something
}

public final native void wait(long timeout) throws InterruptedException;

public final void wait(long timeout, int nanos) throws InterruptedException {
	//do something
}
</pre><br />wari()和wait(long timeout,int nanos)方法都是基于wait(long timeout)方法实现的。同样地，timeout代表毫秒数，nanos代表纳秒数。当调用了某个对象的wait()方法时，当前运行的线程就会转入等待状态（WAITING），等待别的线程再次调用这个对象的notify()或者notifyAll()方法（这两个方法也是本地方法）唤醒它，或者到了指定的最大等待时间，线程自动醒来。如果线程拥有某个或某些对象的同步锁，那么在调用了wait()后，这个线程就会释放它持有的所有同步资源，而不限于这个被调用了wait()方法的对象。wait()方法同样会被Thread类的interrupt()方法中断，并产生一个InterruptedException异常，效果同sleep()方法被中断一样。<br /><br /><em><strong><span style="font-size: medium">实现同步的方式</span></strong></em><br /><br />同步是多线程中的重要概念。同步的使用可以保证在多线程运行的环境中，程序不会产生设计之外的错误结果。同步的实现方式有两种，同步方法和同步块，这两种方式都要用到synchronized关键字。<br /><br />给一个方法增加synchronized修饰符之后就可以使它成为同步方法，这个方法可以是静态方法和非静态方法，但是不能是抽象类的抽象方法，也不能是接口中的接口方法。下面代码是一个同步方法的示例：<br /><pre name="code" class="java">
public synchronized void aMethod() {
	// do something
}

public static synchronized void anotherMethod() {
	// do something
}
</pre><br />线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意一个同步方法时，这个对象的所有同步方法都被锁定了，在此期间，其他任何线程都不能访问这个对象的任意一个同步方法，直到这个线程执行完它所调用的同步方法并从中退出，从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后，其他线程是可以访问这个对象的所有非同步方法的。<br /><br />同步块的形式虽然与同步方法不同，但是原理和效果是一致的。同步块是通过锁定一个指定的对象，来对同步块中包含的代码进行同步；而同步方法是对这个方法块里的代码进行同步，而这种情况下锁定的对象就是同步方法所属的主体对象自身。如果这个方法是静态同步方法呢？那么线程锁定的就不是这个类的对象了，也不是这个类自身，而是这个类对应的java.lang.Class类型的对象。同步方法和同步块之间的相互制约只限于同一个对象之间，所以静态同步方法只受它所属类的其它静态同步方法的制约，而跟这个类的实例（对象）没有关系。<br /><br />下面这段代码演示了同步块的实现方式：<br /><pre name="code" class="java">
public void test() {
	// 同步锁
	String lock = "LOCK";

	// 同步块
	synchronized (lock) {
		// do something
	}

	int i = 0;
	// ...
}
</pre><br />对于作为同步锁的对象并没有什么特别要求，任意一个对象都可以。如果一个对象既有同步方法，又有同步块，那么当其中任意一个同步方法或者同步块被某个线程执行时，这个对象就被锁定了，其他线程无法在此时访问这个对象的同步方法，也不能执行同步块。<br /><br /><em><strong><span style="font-size: medium">synchronized和Lock</span></strong></em><br /><br />Lock是一个接口，它位于Java 5.0新增的java.utils.concurrent包的子包locks中。concurrent包及其子包中的类都是用来处理多线程编程的。实现Lock接口的类具有与synchronized关键字同样的功能，但是它更加强大一些。java.utils.concurrent.locks.ReentrantLock是较常用的实现了Lock接口的类。下面是ReentrantLock类的一个应用实例：<br /><pre name="code" class="java">
private Lock lock = new ReentrantLock();

public void testLock() {
	// 锁定对象
	lock.lock();
	try {
		// do something
	} finally {
		// 释放对对象的锁定
		lock.unlock();
	}
}
</pre><br />lock()方法用于锁定对象，unlock()方法用于释放对对象的锁定，他们都是在Lock接口中定义的方法。位于这两个方法之间的代码在被执行时，效果等同于被放在synchronized同步块中。一般用法是将需要在lock()和unlock()方法之间执行的代码放在try{}块中，并且在finally{}块中调用unlock()方法，这样就可以保证即使在执行代码抛出异常的情况下，对象的锁也总是会被释放，否则的话就会为死锁的产生增加可能。<br /><br />使用synchronized关键字实现的同步，会把一个对象的所有同步方法和同步块看做一个整体，只要有一个被某个线程调用了，其他的就无法被别的线程执行，即使这些方法或同步块与被调用的代码之间没有任何逻辑关系，这显然降低了程序的运行效率。而使用Lock就能够很好地解决这个问题。我们可以把一个对象中按照逻辑关系把需要同步的方法或代码进行分组，为每个组创建一个Lock类型的对象，对实现同步。那么，当一个同步块被执行时，这个线程只会锁定与当前运行代码相关的其他代码最小集合，而并不影响其他线程对其余同步代码的调用执行。<br /><br /><em><strong><span style="font-size: medium">关于死锁</span></strong></em><br /><br />死锁就是一个进程中的每个线程都在等待这个进程中的其他线程释放所占用的资源，从而导致所有线程都无法继续执行的情况。死锁是多线程编程中一个隐藏的陷阱，它经常发生在多个线程共用资源的时候。在实际开发中，死锁一般隐藏的较深，不容易被发现，一旦死锁现象发生，就必然会导致程序的瘫痪。因此必须避免它的发生。<br /><br />程序中必须同时满足以下四个条件才会引发死锁：<br /><ol><li><strong>互斥（Mutual exclusion）：</strong>线程所使用的资源中至少有一个是不能共享的，它在同一时刻只能由一个线程使用。</li><li><strong>持有与等待（Hold and wait）：</strong>至少有一个线程已经持有了资源，并且正在等待获取其他的线程所持有的资源。</li><li><strong>非抢占式（No pre-emption）：</strong>如果一个线程已经持有了某个资源，那么在这个线程释放这个资源之前，别的线程不能把它抢夺过去使用。</li><li><strong>循环等待（Circular wait）：</strong>假设有N个线程在运行，第一个线程持有了一个资源，并且正在等待获取第二个线程持有的资源，而第二个线程正在等待获取第三个线程持有的资源，依此类推……第N个线程正在等待获取第一个线程持有的资源，由此形成一个循环等待。</li></ol><br /><em><strong><span style="font-size: medium">线程池</span></strong></em><br /><br />线程池就像数据库连接池一样，是一个对象池。所有的对象池都有一个共同的目的，那就是为了提高对象的使用率，从而达到提高程序效率的目的。比如对于Servlet，它被设计为多线程的（如果它是单线程的，你就可以想象，当1000个人同时请求一个网页时，在第一个人获得请求结果之前，其它999个人都在郁闷地等待），如果为每个用户的每一次请求都创建一个新的线程对象来运行的话，系统就会在创建线程和销毁线程上耗费很大的开销，大大降低系统的效率。因此，Servlet多线程机制背后有一个线程池在支持，线程池在初始化初期就创建了一定数量的线程对象，通过提高对这些对象的利用率，避免高频率地创建对象，从而达到提高程序的效率的目的。<br /><br />下面实现一个最简单的线程池，从中理解它的实现原理。为此我们定义了四个类，它们的用途及具体实现如下：<br /><ol><li><strong>Task（任务）：</strong>这是个代表任务的抽象类，其中定义了一个deal()方法，继承Task抽象类的子类需要实现这个方法，并把这个任务需要完成的具体工作在deal()方法编码实现。线程池中的线程之所以被创建，就是为了执行各种各样数量繁多的任务的，为了方便线程对任务的处理，我们需要用Task抽象类来保证任务的具体工作统一放在deal()方法里来完成，这样也使代码更加规范。</li><br />Task的定义如下：<br /><pre name="code" class="java">
public abstract class Task {
	public enum State {
		/* 新建 */NEW, /* 执行中 */RUNNING, /* 已完成 */FINISHED
	}

	// 任务状态
	private State state = State.NEW;

	public void setState(State state) {
		this.state = state;
	}

	public State getState() {
		return state;
	}

	public abstract void deal();
}
</pre><li><strong>TaskQueue（任务队列）：</strong>在同一时刻，可能有很多任务需要执行，而程序在同一时刻只能执行一定数量的任务，当需要执行的任务数超过了程序所能承受的任务数时怎么办呢？这就有了先执行哪些任务，后执行哪些任务的规则。TaskQueue类就定义了这些规则中的一种，它采用的是FIFO（先进先出，英文名是First In First Out）的方式，也就是按照任务到达的先后顺序执行。</li><br />TaskQueue类的定义如下：<br /><pre name="code" class="java">
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class TaskQueue {
	private List&lt;Task> queue = new LinkedList&lt;Task>();

	// 添加一项任务
	public synchronized void addTask(Task task) {
		if (task != null) {
			queue.add(task);
		}
	}

	// 完成任务后将它从任务队列中删除
	public synchronized void finishTask(Task task) {
		if (task != null) {
			task.setState(Task.State.FINISHED);
			queue.remove(task);
		}
	}

	// 取得一项待执行任务
	public synchronized Task getTask() {
		Iterator&lt;Task> it = queue.iterator();
		Task task;
		while (it.hasNext()) {
			task = it.next();
			// 寻找一个新建的任务
			if (Task.State.NEW.equals(task.getState())) {
				// 把任务状态置为运行中
				task.setState(Task.State.RUNNING);
				return task;
			}
		}
		return null;
	}
}
</pre><br />addTask(Task task)方法用于当一个新的任务到达时，将它添加到任务队列中。这里使用了LinkedList类来保存任务到达的先后顺序。finishTask(Task task)方法用于任务被执行完毕时，将它从任务队列中清除出去。getTask()方法用于取得当前要执行的任务。<li><strong>TaskThread（执行任务的线程）：</strong>它继承自Thread类，专门用于执行任务队列中的待执行任务。</li><pre name="code" class="java">
public class TaskThread extends Thread {
	// 该线程所属的线程池
	private ThreadPoolService service;

	public TaskThread(ThreadPoolService tps) {
		service = tps;
	}

	public void run() {
		// 在线程池运行的状态下执行任务队列中的任务
		while (service.isRunning()) {
			TaskQueue queue = service.getTaskQueue();
			Task task = queue.getTask();
			if (task != null) {
				task.deal();
			}
			queue.finishTask(task);
		}
	}
}
</pre><li><strong>ThreadPoolService（线程池服务类）：</strong>这是线程池最核心的一个类。它在被创建了时候就创建了几个线程对象，但是这些线程并没有启动运行，但调用了start()方法启动线程池服务时，它们才真正运行。stop()方法可以停止线程池服务，同时停止池中所有线程的运行。而runTask(Task task)方法是将一个新的待执行任务交与线程池来运行。</li><br />ThreadPoolService类的定义如下：<br /><pre name="code" class="java">
import java.util.ArrayList;
import java.util.List;

public class ThreadPoolService {
	// 线程数
	public static final int THREAD_COUNT = 5;

	// 线程池状态
	private Status status = Status.NEW;

	private TaskQueue queue = new TaskQueue();

	public enum Status {
		/* 新建 */NEW, /* 提供服务中 */RUNNING, /* 停止服务 */TERMINATED,
	}

	private List&lt;Thread> threads = new ArrayList&lt;Thread>();

	public ThreadPoolService() {
		for (int i = 0; i &lt; THREAD_COUNT; i++) {
			Thread t = new TaskThread(this);
			threads.add(t);
		}
	}

	// 启动服务
	public void start() {
		this.status = Status.RUNNING;
		for (int i = 0; i &lt; THREAD_COUNT; i++) {
			threads.get(i).start();
		}
	}

	// 停止服务
	public void stop() {
		this.status = Status.TERMINATED;
	}

	// 是否正在运行
	public boolean isRunning() {
		return status == Status.RUNNING;
	}

	// 执行任务
	public void runTask(Task task) {
		queue.addTask(task);
	}

	protected TaskQueue getTaskQueue() {
		return queue;
	}
}
</pre><br /></ol><br />完成了上面四个类，我们就实现了一个简单的线程池。现在我们就可以使用它了，下面的代码做了一个简单的示例：<br /><pre name="code" class="java">
public class SimpleTaskTest extends Task {
	@Override
	public void deal() {
		// do something
	}

	public static void main(String[] args) throws InterruptedException {
		ThreadPoolService service = new ThreadPoolService();
		service.start();
		// 执行十次任务
		for (int i = 0; i &lt; 10; i++) {
			service.runTask(new SimpleTaskTest());
		}
		// 睡眠1秒钟，等待所有任务执行完毕
		Thread.sleep(1000);
		service.stop();
	}
}
</pre><br />当然，我们实现的是最简单的，这里只是为了演示线程池的实现原理。在实际应用中，根据情况的不同，可以做很多优化。比如：<br /><ul><li>调整任务队列的规则，给任务设置优先级，级别高的任务优先执行。</li><li>动态维护线程池，当待执行任务数量较多时，增加线程的数量，加快任务的执行速度；当任务较少时，回收一部分长期闲置的线程，减少对系统资源的消耗。</li></ul><br />事实上Java5.0及以上版本已经为我们提供了线程池功能，无需再重新实现。这些类位于java.util.concurrent包中。<br /><br />Executors类提供了一组创建线程池对象的方法，常用的有一下几个：<br /><pre name="code" class="java">
public static ExecutorService newCachedThreadPool() {
	// other code
}

public static ExecutorService newFixedThreadPool(int nThreads) {
	// other code
}

public static ExecutorService newSingleThreadExecutor() {
	// other code
}
</pre><br />newCachedThreadPool()方法创建一个动态的线程池，其中线程的数量会根据实际需要来创建和回收，适合于执行大量短期任务的情况；newFixedThreadPool(int nThreads)方法创建一个包含固定数量线程对象的线程池，nThreads代表要创建的线程数，如果某个线程在运行的过程中因为异常而终止了，那么一个新的线程会被创建和启动来代替它；而newSingleThreadExecutor()方法则只在线程池中创建一个线程，来执行所有的任务。<br /><br />这三个方法都返回了一个ExecutorService类型的对象。实际上，ExecutorService是一个接口，它的submit()方法负责接收任务并交与线程池中的线程去运行。submit()方法能够接受Callable和Runnable两种类型的对象。它们的用法和区别如下：<br /><ol><li><strong>Runnable接口：</strong>继承Runnable接口的类要实现它的run()方法，并将执行任务的代码放入其中，run()方法没有返回值。适合于只做某种操作，不关心运行结果的情况。</li><li><strong>Callable接口：</strong>继承Callable接口的类要实现它的call()方法，并将执行任务的代码放入其中，call()将任务的执行结果作为返回值。适合于执行某种操作后，需要知道执行结果的情况。</li></ol><br />无论是接收Runnable型参数，还是接收Callable型参数的submit()方法，都会返回一个Future（也是一个接口）类型的对象。该对象中包含了任务的执行情况以及结果。调用Future的boolean isDone()方法可以获知任务是否执行完毕；调用Object get()方法可以获得任务执行后的返回结果，如果此时任务还没有执行完，get()方法会保持等待，直到相应的任务执行完毕后，才会将结果返回。<br /><br />我们用下面的一个例子来演示Java5.0中线程池的使用：<br /><pre name="code" class="java">
import java.util.concurrent.*;

public class ExecutorTest {
	public static void main(String[] args) throws InterruptedException,
			ExecutionException {
		ExecutorService es = Executors.newSingleThreadExecutor();
		Future fr = es.submit(new RunnableTest());// 提交任务

		Future fc = es.submit(new CallableTest());// 提交任务
		// 取得返回值并输出
		System.out.println((String) fc.get());

		// 检查任务是否执行完毕
		if (fr.isDone()) {
			System.out.println("执行完毕-RunnableTest.run()");
		} else {
			System.out.println("未执行完-RunnableTest.run()");
		}

		// 检查任务是否执行完毕
		if (fc.isDone()) {
			System.out.println("执行完毕-CallableTest.run()");
		} else {
			System.out.println("未执行完-CallableTest.run()");
		}

		// 停止线程池服务
		es.shutdown();
	}
}

class RunnableTest implements Runnable {
	public void run() {
		System.out.println("已经执行-RunnableTest.run()");
	}
}

class CallableTest implements Callable {
	public Object call() {
		System.out.println("已经执行-CallableTest.call()");
		return "返回值-CallableTest.call()";
	}
}
</pre><br />运行结果：<br /><ol><li>已经执行-RunnableTest.run()</li><li>已经执行-CallableTest.call()</li><li>返回值-CallableTest.call()</li><li>执行完毕-RunnableTest.run()</li><li>执行完毕-CallableTest.run()</li></ol><br />使用完线程池之后，需要调用它的shutdown()方法停止服务，否则其中的所有线程都会保持运行，程序不会退出。<br /><br /><strong>下期预告：JAVA面试题解惑系列（十一）——“++”和“--”，看你晕不晕</strong>
          <br/>
          <span style="color:red;">
            <a href="http://zangweiren.javaeye.com/blog/225949#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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>Fri, 08 Aug 2008 18:55:14 +0800</pubDate>
        <link>http://zangweiren.javaeye.com/blog/225949</link>
        <guid>http://zangweiren.javaeye.com/blog/225949</guid>
      </item>
      <item>
        <title>MySQL中的各种JOIN(CROSS JOIN, INNER JOIN, LEFT [OUTER]</title>
        <author>ZL58</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://zl58.javaeye.com">ZL58</a>&nbsp;
          链接：<a href="http://zl58.javaeye.com/blog/225927" style="color:red;">http://zl58.javaeye.com/blog/225927</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          MySQL中的各种JOIN<br /><br />1. 笛卡尔积(交叉连接)<br />在MySQL中可以为CROSS JOIN或者省略CROSS即JOIN，或者使用’,’<br />如<br />SELECT * FROM table1 CROSS JOIN table2<br />SELECT * FROM table1 JOIN table2<br />SELECT * FROM table1,table2<br /><br />由于其返回的结果为被连接的两个数据表的乘积，因此当有WHERE, ON或USING条件的时候一般不建议使用，因为当数据表项目太多的时候，会非常慢。<br />一般使用LEFT [OUTER] JOIN或者RIGHT [OUTER] JOIN<br /><br />2. 内连接INNER JOIN<br />在MySQL中把INNER JOIN叫做等值连接，即需要指定等值连接条件<br />在MySQL中CROSS和INNER JOIN被划分在一起，不明白。<br />参看MySQL帮助手册<br />http://dev.mysql.com/doc/refman/5.0/en/join.html<br />join_table:<br />    table_reference [INNER | CROSS] JOIN table_factor [join_condition]<br /><br />3. MySQL中的外连接，分为左外连接和右连接，<br />即除了返回符合连接条件的结果之外，还要返回左表(左连接)或者右表(右连接)中不符合连接条件的结果，相对应的使用NULL对应。<br /><br />a. LEFT [OUTER] JOIN<br />SELECT column_name FROM table1 LEFT [OUTER] JOIN table2 ON table1.column=table2.column<br />除了返回符合连接条件的结果之外，还需要显示左表中不符合连接条件的数据列，相对应使用NULL对应<br /><br />b. RIGHT [OUTER] JOIN<br />SELECT column_name FROM table1 RIGHT [OUTER] JOIN table2 ON table1.column=table2.column<br />RIGHT与LEFT JOIN相似不同的仅仅是除了显示符合连接条件的结果之外，还需要显示右表中不符合连接条件的数据列，相应使用NULL对应<br /><br />——————————————–<br />添加显示条件WHERE, ON, USING<br />1. WHERE子句<br />2. ON<br />3. USING子句，如果连接的两个表连接条件的两个列具有相同的名字的话可以使用USING<br />例如<br />SELECT &lt;column_name> FROM &lt;table1> LEFT JOIN &lt;table2> USING (&lt;column_name>)<br /><br />连接多余两个表的情况<br />举例：<br />mysql> SELECT  artists.Artist, cds.title, genres.genre<br />    -> FROM cds<br />    -> LEFT JOIN genres<br />    -> ON cds.genreID = genres.genreID<br />    -> LEFT JOIN artists<br />    -> ON cds.artistID = artists.artistID;<br />或者<br />mysql> SELECT artists.Artist, cds.title, genres.genre<br />    -> FROM cds<br />    -> LEFT JOIN genres<br />    -> ON cds.genreID = genres.genreID<br />    -> LEFT JOIN artists<br />    -> ON cds.artistID = artists.artistID<br />    -> WHERE (genres.genre = ‘Pop’);<br />——————————————–<br /><br />另外需要注意的地方<br /><br />在MySQL中涉及到多表查询的时候，需要根据查询的情况，想好使用哪种连接方式效率更高。<br />1. 交叉连接(笛卡尔积)或者内连接<br />[INNER | CROSS] JOIN<br />2. 左外连接LEFT [OUTER] JOIN或者右外连接RIGHT [OUTER] JOIN<br /><br />注意指定连接条件WHERE, ON，USING.
          <br/>
          <span style="color:red;">
            <a href="http://zl58.javaeye.com/blog/225927#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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>Fri, 08 Aug 2008 17:21:51 +0800</pubDate>
        <link>http://zl58.javaeye.com/blog/225927</link>
        <guid>http://zl58.javaeye.com/blog/225927</guid>
      </item>
      <item>
        <title>一个递归例子</title>
        <author>ahrhu</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahrhu.javaeye.com">ahrhu</a>&nbsp;
          链接：<a href="http://ahrhu.javaeye.com/blog/225924" style="color:red;">http://ahrhu.javaeye.com/blog/225924</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">
public class recursion1{
	
	int fib(int n){   
	    if(n == 0)        return 0;   
	    else if(n == 1)   return 1;  
	    else             
		System.out.print("  "+n);
		int a = fib(n-1);
		int b = fib(n-2);
		int c = a+b;
		System.out.print(" a="+a+" b="+b+" a+b= "+c+"\\ ");
		System.out.println();	
		return fib(n-1) + fib(n-2);  
	
}
 
	public static void main(String args[]){
			
 		dgClass1 dg = new dgClass1();
		System.out.println(" 6="+dg.fib(6));  //return 8



	}

}<pre name="code" class="java"></pre><br /></pre>
          <br/>
          <span style="color:red;">
            <a href="http://ahrhu.javaeye.com/blog/225924#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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>Fri, 08 Aug 2008 17:09:25 +0800</pubDate>
        <link>http://ahrhu.javaeye.com/blog/225924</link>
        <guid>http://ahrhu.javaeye.com/blog/225924</guid>
      </item>
      <item>
        <title>Lucene索引的建立</title>
        <author>狂放不羁</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yuquan-nana.javaeye.com">狂放不羁</a>&nbsp;
          链接：<a href="http://yuquan-nana.javaeye.com/blog/225906" style="color:red;">http://yuquan-nana.javaeye.com/blog/225906</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Lucene索引的建立需要两种基础的结构支持，它们分别是Document和Field，这两个概念不能从字面意思上理解。其实Document是Field的集合，它其实主要是用来管理Field的，而Field是一种数据源，也就是要索引的东西。每个Field都有几个重要的属性，它们分别是：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 1 name:它是这个Field的名字。它主要是以后要搜索的时候用的，也就是要按照此名字来对此Field来检索。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 2 value：它是Field的具体的内容。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 3 store：它表明此Field是否需要存储，如果通过名字检索此Field后，要显示完整的Field的内容，那么就要将起属性设置为YES。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 4 index：它表明此Field是否需要索引。它可以有四种不同的值。分别是不索引，索引并且分词，索引不分词，以及不使用分析器来索引。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 搞清楚了Document和Field具体是什么后，就可以了解Lucene索引建立的具体过程了。索引的建立主要是有类IndexWriter来实现的，而实际上索引建立的工作是由DocumentWriter来完成的，IndexWriter主要是将索引中加入已经建立好的Document.通过DocumentWriter建立索引后，建立了如下的文件：segments,deletable,.f, .fdt,.fdx,.fnm,.frq,.prx,.tii,.tis。</p>
<p>&nbsp;&nbsp;&nbsp; 1&nbsp; segments文件主要记录了索引中有多少个segment，以及每个segment有多少个Document等信息。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;2. fdt文件主要保存了Field的内容，并且它只保存Field属性被设置为YES的Field。</p>
<p>&nbsp;&nbsp;&nbsp; 3 .fdx文件则记录了Document在.fdt文件中的位置。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp; .fnm文件记录了此segment中所有Document的所有Field的名字。</p>
          <br/>
          <span style="color:red;">
            <a href="http://yuquan-nana.javaeye.com/blog/225906#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Aug 2008 16:06:28 +0800</pubDate>
        <link>http://yuquan-nana.javaeye.com/blog/225906</link>
        <guid>http://yuquan-nana.javaeye.com/blog/225906</guid>
      </item>
      <item>
        <title>推荐一篇译文：保持专注的10个小技巧</title>
        <author>txswei</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tanw.javaeye.com">txswei</a>&nbsp;
          链接：<a href="http://tanw.javaeye.com/blog/225878" style="color:red;">http://tanw.javaeye.com/blog/225878</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我相信能掌握一些保持专注的技巧很重要。如果你有定力能够安静地坐在台前全神贯注投入工作长达几个小时，我敢保证，在这样的状态下，即使是半个小时的工作，也一 定比不断分心常遭干扰的一天的工作能取得更多的成果。 <br /><br /><strong>一、杜绝干扰 </strong><br /><br />很明显，周遭的干扰足以让你在工作中心神不宁，那么你排除了这些干扰吗？不得不承认，运行自动收 信软件（译者注：如Gmail Notifier、MSN Messenger，一般电子邮件客户端也具有此项功能，如：Mozilla Thunderbird、Microsoft Outlook、Foxmail等）能保证你在第一时间阅读来信，开着即时通讯软件（译者注：IM即Instant Messenger，如ICQ、MSN Messenger、Gtalk等）有一句没一句地回复网友们的招呼能消减工作时的寂寞，但是它们都是随时能打断工作的干扰源，你需要杜绝它们。 <br /><br /><br />一般进入专注状态需要15分钟时间，如果每5分钟就要被打断一次，你又如何能够聚精会神？所以，请特别安排划分一段时间以供查收和处理邮件，请尽量避免在电话及来访繁忙的时段安排工作，并告诉相关的人不要在你工作时打扰你。 <br /><br /><strong>二、安排合适的工作场合 </strong><br /><br />工作环境对你能否专注工作有很大的影响。请把自己的座位安排在能方便够及干扰源的地方，比如正对门窗，比如让电话顺手可及，如此一来，简单无意的一瞥即可排除门窗产生的杂音，顺手一拿即可接听电话，这样才能使你的分心降低到最小程度。 <br /><br /><strong>三、明确目标</strong> <br /><br />请在工作开始前就明确自己的目标。如果你对自己需要完成什么工作都不知道，这种困扰会严重影响专注程度。在作文之前，我会先想明白文章的主题并在脑里陈列提纲打个粗稿。少了“明确目标”这一步往往会导致部分工作的返工。 <br /><br /><strong>四、理出头绪 </strong><br /><br />脑里七七八八的一大摞任务很难让你全神贯注。在工作开始前，除了要明确目标以外，你还应该花上几分钟时间为大堆的零碎任务理出头绪分清秩序，否则你就得在工作中浪费几个小时来处理任务间的混乱和冲突了。 <br /><br /><strong>五、弄清任务要求 </strong><br /><br />请在处理任务之前弄清它的指标和要求，比如：任务的质量要求；任务的标准要求；以及任务进行过 程中会受到怎样的限制。如果你是个程序员，你需要搞清所写代码的注释语句密度标准（译者注：注释语句，是为了增加代码可读性的解释性语句，不会被编译器翻 译成可执行的机器代码。），需要调用哪些函数（译者注：函数，是程序的基本单元，是被封装起来能完成特定功能的代码段。对于C或者Fortran之类的非 高级编程语言，明确了需要调用的函数，你就可以在程序开始处将相关的函数文件包含进来，以提高编程效率。），程序的灵活性要求如何等等。如果你在作文，在 这之前就需首先为自己指定好行文风格并做出文书长度的规定。如果在任务开始之初没有弄清任务要求，就会引起无规则行事的混乱局面，那时你将在过程中不断考 虑和转变这些要求而不停返工，不但工作流程无法顺畅执行，这些停断也会使你无法专注。 <br /><br /><strong>六、指定时限 </strong><br /><br />为自己强行指定时限完成任务对于工作专注度的影响有好也有坏。一个存在于意识中的时限能够使你忘记琐碎的小事并提升你的工作速度。如果你规定自己一小时之内必须完成一个徽标的设计，就能避免自己使用花里胡哨的废装赘饰，从而使徽标保持简洁清爽的面目并提高设计效率。 <br /><br />时限也会令你陷入无法按时完成工作的焦虑而难以专心于手头实际的工作，所以我建议只在这些情况下为自己设定时限： <br /><br />任务时间有限。如果你需要在一天内完成一个可能花费几周时间的工作，就该为任务划分成块，分别设定时限，如此才能保证在短时间内完成任务的重要部分。 <br />当你遇上那些非常容易扩展伸延的任务。如果你的任务很容易延伸扩展出其他的要求并不断产生子任务，时限可以使你更好地控制进度而不至于东奔西走陷入混乱。 <br />避免拖延和耽搁。当你担心自己的困怠是否会耽误任务进度时，设定一个时限就是为自己安排了一个监工。 <br /><br /><strong>七、隔离自己 </strong><br /><br />除非需要团队合作，否则请你在工作中做个隐士，在闹哄哄的工作环境中隔离自己，构筑一个私人空间，必要时给门贴上“工作中，拒绝打扰”的标识，拔掉电话线，直到工作完成再去与人闲聊攀谈。构建这样的工作环境才能使你更好地专注于工作。 <br /><br /><strong>八、清除障碍 </strong><br /><br />工作中遇到棘手的问题时难免就会碰上障碍，当你思路受阻时必定心烦意乱难以专注。这时你需要纸笔进行头脑风暴，即便在遇到障碍时灰心丧气，写下思路却能使你依然保持精神的集中而不至于心猿意马。 <br /><br /><strong>九、健康能够驱动头脑飞转 </strong><br /><br />身体状况决定了专注程度。没人会指望一个醉醺醺的家伙能百分百地投入工作。长期睡眠不足；过度 使用兴奋药物（比如咖啡因）；酽饮浓食；摄入过多能量，这些都会影响你集中注意的能力。请戒绝其中某个不良的生活习惯，保持一个月，看看你的体质是否得到 改善，我的个人经验是，只需要改变一丁点儿生活行为，就可以大幅提高专心能力。 <br /><br /><strong>十、保持耐心 </strong><br /><br />在把手指放上键盘开始作文之前，我通常会在座位上安坐15～20分钟以使自己心定气闲，在这期间我总有一股站起身来离开的冲动，但我会说服自己：只有保持耐心才能专心致志地卯定文题，思流顺畅，文路扩扬。 <br /><br />如果需要保持集中的心思专注，我建议你把工作以90～120分钟为界分割成段。这个分段的长短因人而异，需要根据自身特点进行设计，时间短了则会使你浪费过多时间来调节自己进入工作状态，反之则会因为工作时间太长而降低效率并分散注意力。 <br /><br />原文地址：<a href="http://chn.blogbeta.com/298.html" target="_blank">10 Tips for Razor Sharp Concentration </a>翻译：Angelived
          <br/>
          <span style="color:red;">
            <a href="http://tanw.javaeye.com/blog/225878#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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>Fri, 08 Aug 2008 15:08:45 +0800</pubDate>
        <link>http://tanw.javaeye.com/blog/225878</link>
        <guid>http://tanw.javaeye.com/blog/225878</guid>
      </item>
      <item>
        <title>Solaris10之配置subversion</title>
        <author>单身男人</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://h-jingfeng.javaeye.com">单身男人</a>&nbsp;
          链接：<a href="http://h-jingfeng.javaeye.com/blog/225848" style="color:red;">http://h-jingfeng.javaeye.com/blog/225848</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          越来越不喜欢windows了，无论从速度、安全还是其他方面。也许最大的原因是用的太久了。人可能总是需要一些新的感觉才会觉得人生才会更完整吧。<br />在Solaris10下配置我的开发环境<br />系统：SunOS sol-sso 5.10 Generic_127128-11 i86pc i386 i86pc<br />内存：2G<br />JDK：系统自带的1.5.0_14<br />目标：配置完成netbeans+svn<br /><br />1、下载netbeans（当然了你可以使用非常好用的Eclipse）<br />netbeans的下载和安装没什么特别注意的地方，从sun官方网站上下，安装运行即可。<br /><br />2、配置svn<br />整整实验了1天才搞定，主要是对solaris10系统的不熟悉造成的，这中间，一个非常重要的技术网站能提供非常大的帮助：<a href="http://www.sunfreeware.com" target="_blank">http://www.sunfreeware.com</a><br /><br />不过问题一个接着一个来了，svn要依赖apache，只能先安装apache httpd，同样的，还需要下面的相关库:<br /><br />1) gcc-3.4.6-sol10-x86-local.gz<br />2) libiconv-1.11-sol10-x86-local.gz<br />3) libintl-3.4.0-sol10-x86-local.gz<br />4) neon-0.25.5-sol10-x86-local.gz<br />5) openssl-0.9.8h-sol10-x86-local.gz<br />6) expat-2.0.1-sol10-x86-local.gz<br />7) apache-2.2.6-sol10-x86-local.gz<br />8) subversion-1.4.5-sol10-x86-local.gz<br /><br />这些都是压缩格式的文件，逐一解压文件得到安装包文件。solaris系统自带的有gcc，不过版本是3.4.2，在/usr/sfw/bin下，根据上面版本软件的相关依赖，必须要安装3.4.6的gcc，更多期间的依赖关系参考上面的网站相关内容。<br /><br />2.1 安装软件：使用pkgadd命令<br /><br />在解压缩文件所在目录，打开终端，输入pagadd命令：如<br /><br /># pkgadd -d apache-2.2.6-sol10-x86-local 回车执行命令<br />这样，apache包将会被安装到系统的/usr/local/apache2目录下<br /><br />同理，安装上述的几个包软件<br /><br />svn被默认安装到：/usr/local/bin目录下<br /><br />当然了，如果你觉得有问题，可以卸载你安装的某个版本的软件<br /><br />2.2 查询和卸载软件<br /><br />使用 pkginfo 命令查找相关软件，以apache为例<br /><br />bash-3.00# pkginfo | grep 'apache'<br />application SMCap226                         apache<br /><br />使用pkgrm 命令删除软件，以上面的查询结果为例<br />bash-3.00# pkgrm SMCap226 回车执行命令<br /><br />根据提示完成软件包的卸载。<br /><br /><br />3、这些包，默认的都被安装在/usr/local下，因此需要把 /usr/local/bin加入到你的系统PATH中<br /><br />一般的，在普通用户的目录下，都会有个 .profile文件，编辑其PATH，如：<br /><br />PATH=$PATH:/usr/ccs/bin:/usr/local/bin:/etc:.<br />export PATH<br /><br />如果使用的是root用户，默认的，系统不会在根目录下创建这个 .profile文件，你可以从/etc/skel目录下，拷贝local.profile文件到 / 下，改名为 .profile<br /><br />bash-3.00# source /.profile命令，或者 注销从新登陆，环境变量即可生效<br /><br />4、启动netbeans，指定svn的目录：/usr/local/bin即可用netbeans的svn功能了<br /><br />可见，没有什么特别需要配置的地方，主要是solaris10中的一些基本管理，如环境变量、包管理、解压缩等等。
          <br/>
          <span style="color:red;">
            <a href="http://h-jingfeng.javaeye.com/blog/225848#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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>Fri, 08 Aug 2008 14:15:48 +0800</pubDate>
        <link>http://h-jingfeng.javaeye.com/blog/225848</link>
        <guid>http://h-jingfeng.javaeye.com/blog/225848</guid>
      </item>
      <item>
        <title>重建UBUNTU的SWAP分区</title>
        <author>lveyo</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lveyo.javaeye.com">lveyo</a>&nbsp;
          链接：<a href="http://lveyo.javaeye.com/blog/225802" style="color:red;">http://lveyo.javaeye.com/blog/225802</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          用ghost恢复ubuntu各个分区之后，发现swap分区不能用了。<br /><br />于是用分区软件(GParted)查看swap分区为/dev/sdb5<br /><br />在终端下执行<br /><br />sudo mkswap /dev/hdbX #X是swap分区<br /><br />执行完会显示：<br /><br />Setting up swapspace version 1, size = 1587441 kB<br />no label, UUID=3e509cbc-96b4-4347-aa87-b1d039fbea35<br /><br />记录这个UUID，编辑/etc/fstab<br />把swap分区对应的UUID修改为新的UUID，重启动系统，swap分区就被激活了。
          <br/>
          <span style="color:red;">
            <a href="http://lveyo.javaeye.com/blog/225802#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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>Fri, 08 Aug 2008 13:01:42 +0800</pubDate>
        <link>http://lveyo.javaeye.com/blog/225802</link>
        <guid>http://lveyo.javaeye.com/blog/225802</guid>
      </item>
      <item>
        <title>Perl学习总结</title>
        <author>agen_0502</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://agen-0502.javaeye.com">agen_0502</a>&nbsp;
          链接：<a href="http://agen-0502.javaeye.com/blog/225797" style="color:red;">http://agen-0502.javaeye.com/blog/225797</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><span style="color: red; font-size: small;">1    Perl基础学习总结</span>
</p>
<p><br />
<span style="color: #ff0000;">1.1  Perl的安装和环境变量</span>
<br />
       在装Linux系统时，Perl会自动被安装到你的系统中（默认）, 而我为了学习，不得不把自带的Perl安装程序卸载, 再从官方下载相关的源码安装，安装虽说简单，但我还是把安装遇到的问题跟大家分享一下:<br />
下载源码包后，将其解压, 大概操作如下:  <br />
&nbsp;&nbsp;&nbsp;
    # Tar &ndash;Zxvf  Perl-Xxx.Tar.Gz <br />
&nbsp;&nbsp;&nbsp;
    # Cd Perl-Xxx<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      ./Configure <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      Make <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      Make Install<br />
&nbsp;&nbsp;
在Configure后面有很多的参数，如果不想安装在指定的路径下，可用-D加上Profix=/You/Path/<br />
其他参数的说明可用--Help参数查看. 具体的详细安装就要看README. 安装后要验证，用Perl &ndash;V命令，如果没有出错的话，就会显示在当前系统的Perl版本信息. 如果说未找到此命令,就要配置环境变量了，<br />
在/Etc/Profile文件的最后一行加上PATH=/You/Path/Bin:$PATH, 用Source 或重启系统就行了.</p>
<p><br />
<span style="color: #ff0000;"><span>1.2  Perl标量总结</span>
</span>
<br />
&nbsp;&nbsp;
       在Perl 里面，标量其实就是数字，字符串，简称为基本数据类型 , 其中数字又分为整型，浮点型；而字符串则是由单引号或双引号组成的. 对于这些整型和浮点型及字符串它们之间的操作，我这里就不多讲，举个很简单的例子给大家看看，把一个整数变成一个浮点点只需要乘以1或除以1，因为乘法或除法就是一种浮点操作。把数字变成字符串就更简单了。Printf &lsquo;%D/%D/%D&rsquo;,2008,07,27;  这个打印语句会显示<br />
2008-7-27	上面用%后加字母D，就叫数字格式化。具体请查相关文档.</p>
<p><br />
<span style="color: #ff0000;">1.3	Perl 操作符简单学习</span>
<br />
Perl的操作符相当之多，我把学习中遇到的一些常用的操作符跟大家分享。</p>
<p><br />
&nbsp;&nbsp;&nbsp;
        赋值操作符 &nbsp; &nbsp; &nbsp; &nbsp;         =                       <br />
&nbsp;&nbsp;&nbsp;
        算术操作符&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;         + &nbsp;  -&nbsp;  *&nbsp;  / &nbsp;  **&nbsp;   %<br />
&nbsp;&nbsp;&nbsp;
        字符串操作符 &nbsp; &nbsp; &nbsp;    .     X                                #点是连接操作符, X是复制操作符<br />
&nbsp;&nbsp;&nbsp;
        逻辑操作符 &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;         AND    OR  NOT    &amp;&amp;    ||    !<br />
&nbsp;&nbsp;&nbsp;
        组合赋值操作符&nbsp;&nbsp;&nbsp; +=&nbsp;  -=&nbsp; *=&nbsp; /=&nbsp; **=&nbsp; %=&nbsp;  ||=&nbsp;     &amp;&amp;=&nbsp;    X=&nbsp;   .=  <br />
&nbsp;&nbsp;&nbsp;
        递增和递减操作符         ++ &nbsp;    --<br />
&nbsp;&nbsp;&nbsp;
        比较操作符&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;         ==&nbsp;   !=&nbsp;     &gt;&nbsp;   &lt;&nbsp;   &lt;=&nbsp;    &gt;=&nbsp;   &lt;=&gt;&nbsp; eq&nbsp; ne&nbsp; gt&nbsp; lt &nbsp; le&nbsp; ge &nbsp; cmp</p>
<p><br />
        上面除了&lt;=&gt; Cmp外，其他全返回Ture 或Flase .  &lt;=&gt;和Cmp 返回三个值 1/0/-1<br />
&nbsp;&nbsp;
         绑定操作符       =~   !~<br />
&nbsp;&nbsp;
         关系操作符        =&gt;    #  在Hash中常用到<br />
&nbsp;&nbsp;
         范围操作符        ..       #  1..10     代表 1 2 3 4 5 6 7 8 9 10        <br />
&nbsp;&nbsp;
          三元操作符       ? :  <br />
上面就是一些常用到的操作符，当然这只是Perl当中的一小部分，还有很多的操作符由于缺乏经验，所以我也不知道。</p>
<p><br />
<br />
<span style="font-size: small;"><span style="color: red;">2	Perl 数组和Hash的总结
</span>
</span>
</p>
<p><br />
&nbsp;&nbsp;&nbsp;
数组其实就是列表的组合,而列表其实就是数据的集合,  如何定义数组，我用例子来说，大家看的会更明白一点. 首先数组用@来做前缀的，例如:  @Array=(1,2,3,4,5);     <br />
&nbsp;&nbsp;&nbsp;
用括号括起来 的就是一个列表，然后把它赋值给@Array这个数组, 可以通过数组下标来取数组元素，数组下标从0开始, 下面是一些操作数组的常用函数:</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
push  是往数组尾部添加一个元素，<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
pop是往数组尾部删除一个元素，<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
shift 是往数组首部添加一个元素，<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
unshift是往数组首部删除一个元素。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
splice函数是删除数组多个元素   <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
undef 是删除数组所有元素。还可以利用Reverse 和Sort对数组反转和排序. </p>
<p>&nbsp;&nbsp; 另外，获得数组的元素大小也有好几种方法:  $Size=@Array    $#Array+1   Scalar(@Array)  , 这里要解释一下$#Array+1 ，$#是取得当前数组最后一个元素的下标，所以要加1. 对数组的了解我就这么多，下面说说hash:<br />
定义hash要用%做前缀, hash是由键值组合的,在Perl 里面用&rdquo;=&gt;&rdquo;操作符来区分hash的键和值,  操作hash是一个很复杂的过程，我这里写一些比较简单的,hash操作函数:  </p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp; keys 是取hash中的key, 而values 函数是取hash中的value，在hash中，key必须是唯一的。<br />
&nbsp;&nbsp;&nbsp;&nbsp;
each函数是返回hash中的key/value对，每次只返回一个键值对. <br />
&nbsp;&nbsp;&nbsp;&nbsp;
exists函数 判断hash是否存在某个键或值是否存在, 一般是判断key是否存在<br />
&nbsp;&nbsp;&nbsp;&nbsp;
undef 函数是删除一个键对应的值，而这个键会一直保存在hash中.<br />
&nbsp;&nbsp;&nbsp;&nbsp;
delete函数是整个删除key和value.<br />
&nbsp;&nbsp;
也可以使用前面提到的reverse函数把整个hash反转. 总之对hash要想深入了解，必须要多实践，看懂不代表会做，对hash我暂且也只了解这么一点。<br />
<br />
<span style="color: #ff0000; font-size: small;">3.	Perl 正则表达式</span>
<br />
正则表达式是Perl的特征之一，也相当复杂，我把学会的跟大家分享：<br />
&nbsp;&nbsp;
首先要说三个符号:     $  @ \ <br />
这三个字符如果转义的话，就会马上触发文本的插值操作. 还有一些特殊的字符，但由于比较多，就说几个常见的  \r  \n \t      分别是回车，换行，制表符.</p>
<p><br />
&nbsp;&nbsp;
要应用正则一般用//作为分隔符，当然还可以使用其他自己喜欢的字符做为分隔符, 使用绑定操作符&rdquo;=~&rdquo;可以拿左边的字符串和右边的模式匹配，正则表达式的操作符如下：<br />
&nbsp;&nbsp;&nbsp;&nbsp;
m//&nbsp;&nbsp;&nbsp;&nbsp;    匹配操作符<br />
&nbsp;&nbsp;&nbsp;&nbsp;
s//&nbsp;&nbsp;&nbsp;&nbsp;      替换操作符</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;
修饰符如下:<br />
&nbsp;&nbsp;&nbsp;&nbsp;
/i&nbsp;&nbsp;&nbsp;&nbsp;        忽略大小写<br />
&nbsp;&nbsp;&nbsp;&nbsp;
/g&nbsp;&nbsp;&nbsp;&nbsp;       匹配全局</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;
还有在匹配模式中的一些常用特殊符号: <br />
&nbsp;&nbsp;&nbsp;&nbsp;
?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 匹配零个或一个<br />
&nbsp;&nbsp;&nbsp;&nbsp;
*&nbsp;&nbsp; &nbsp; &nbsp;          匹配零个或多个<br />
&nbsp;&nbsp;&nbsp;&nbsp;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;          匹配一个或多个<br />
对于正则表达式我就说这么多，因为内容实在太多了，我也只学了一点皮毛。</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://agen-0502.javaeye.com/blog/225797#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</span></a></li><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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Aug 2008 12:55:18 +0800</pubDate>
        <link>http://agen-0502.javaeye.com/blog/225797</link>
        <guid>http://agen-0502.javaeye.com/blog/225797</guid>
      </item>
      <item>
        <title>如何给 GlassFish Open Bug</title>
        <author>judytang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://glassfishquality.javaeye.com">judytang</a>&nbsp;
          链接：<a href="http://glassfishquality.javaeye.com/blog/225791" style="color:red;">http://glassfishquality.javaeye.com/blog/225791</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          GlassFish 是开源产品，开源产品需要有个开源的方法来Open Bug，每当发现问题，任何一个用户都能 Open Bug，这是产品开发必不可少的环节，下面是具体步骤。<br /><br />(1) 点击注册 java.net ID: <a href="https://www.dev.java.net/servlets/Join" target="_blank">https://www.dev.java.net/servlets/Join</a><br /><br />(2) 点击<a href="https://glassfish.dev.java.net/servlets/ProjectIssues" target="_blank">https://glassfish.dev.java.net/servlets/ProjectIssues</a>，进入后到右上角登录，然后就能 Open Bug 了。选择DEFECT，然后把几个FIELD填写上，特别重要的是填写下面每个细节, 然后点击 Submit Issue. <br /><br />- 你用的是GlassFish是什么Version<br />- 你用的JDK是什么Version<br />- 你用的OS是什么<br />- 出错的步骤是什么，要写得非常详细，好让开发人员做同样的步骤来DEBUG<br />- 附上APPLICATION，这个要先OPEN BUG后，才能有ATTACH的功能
          <br/>
          <span style="color:red;">
            <a href="http://glassfishquality.javaeye.com/blog/225791#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Aug 2008 12:50:42 +0800</pubDate>
        <link>http://glassfishquality.javaeye.com/blog/225791</link>
        <guid>http://glassfishquality.javaeye.com/blog/225791</guid>
      </item>
      <item>
        <title>单点登录(Single Sign On)设计实践</title>
        <author>ginge</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ginge.javaeye.com">ginge</a>&nbsp;
          链接：<a href="http://ginge.javaeye.com/blog/225778" style="color:red;">http://ginge.javaeye.com/blog/225778</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp;&nbsp;&nbsp; 转到Architecture Team后接手了公司的Framework,&nbsp;单点登录也就落到我头上了.</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 公司最开始使用的策略是基于Tomcat 的Realm SSO, 这种策略的缺点是所有应用都必须放在同一个Tomcat下面, 放到不同Tomcat下都必须再做一次登录.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 非常不利于HA.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; (一)转到Architecture Team后我就向SDC的头提议了CAS的SSO策略.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><img src="http://www.javaeye.com/upload/picture/pic/19561/8edd800e-6822-3af8-8d74-0f2feb0c5dd4.jpg?1218167350" height="626" alt="" width="684" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">1)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">The user choose to visit AMS</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">2)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">When the user arrived, AMS will first check to see if the user is authenticated by trying to locate a service ticket. if not, the framework on which the AMS built will redirect the user to the login page</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">3)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">Then Framework will ask the user to enter username and password which can identify herself/himself</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">4)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">If authentication passed, the user will be redirected back to AMS</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">5)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">The user is granted to visit AMS</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">6)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">If at some time during the same session the user serfs the BMS&rsquo; page with a ticket</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">7)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">BMS will first check to see if the user is authenticated by trying to locate a service ticket. And it found one, but it doesn&rsquo;t know whether it&rsquo;s valid, so it ask CAS to check it out</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">8)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">Validation pass</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span style="font-size: small;"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">9)<span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">BMS is granted to visit.</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"></span><span style="font-size: small;">&nbsp;</span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"></span><span style="font-size: small;">&nbsp;</span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; <span style="font-family: Verdana;">检验一种方法的好坏是看它是否满足实际需要, 这种策略扩展性很好, 但是却需要独立部署一个中央认证系统. 公司非常不希望增加一个潜在的单点故障. 所以这个策略没被采纳.</span></span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-family: Verdana;"></span></span></span><span style="font-size: small;">&nbsp;</span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-family: Verdana;"></span></span></span><span style="font-size: small;">&nbsp;</span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-size: small; font-family: Verdana;">&nbsp;&nbsp; (二)&nbsp;不知什么原因, 后来SSO被外包公司负责. 他们提供了另外一种方案, 还是基于Tomcat, 利用Domain Cookie, 修改自己的Realm Authenticator, 可以提供真正的SSO体验. 最大的缺点是不同的Web Container要实现一整套不同的实现.</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-family: Verdana;"></span></span></span><span style="font-size: small;">&nbsp;</span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-family: Verdana;"></span></span></span><span style="font-size: small;">&nbsp;</span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-size: small;"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-family: Verdana;">&nbsp;&nbsp; (三) 我自然对方案(二)很不满意. 吸取(二)的优点, 提出了以下方案. 基于Spring Security 2.0, 提供自己的SSOTicketFilter和SSOAuthenticationFilter. SSOAuthenticationFilter负责本地用户认证, SSOTicketFilter负责跨系统用户认证. </span></span></span><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-family: Verdana;">SSOAuthenticationFilter在用户通过验证后保存Ticket. 跨系统时另一个系统由于也是采用Framework, 它也就会用同样的SSOTicketFilter实现来检验用户提供的Ticket. 经过验证, 方案(三)能够提供真正的SSO体验, 公司最终采取了这个方案.</span></span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-family: Verdana;"></span></span></span><span style="font-size: small;">&nbsp;</span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-family: Verdana;"></span></span></span><span style="font-size: small;">&nbsp;</span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-size: small; font-family: Verdana;">&nbsp; 以下样例配置:</span></span></span></p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-size: x-small; font-family: Verdana;"></span></span></span></p>
<p><span style="font-family: Times New Roman;"><span lang="EN-US"><span style="font-size: x-small; font-family: Verdana;">
<pre name="code" class="java">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd"&gt;
                        
                        
	&lt;beans:bean id="springSecurityFilterChain"
		class="org.springframework.security.util.FilterChainProxy"&gt;
		&lt;beans:property name="filterChainMap"&gt;
			&lt;beans:map&gt;
				&lt;beans:entry key="/**"&gt;
					&lt;beans:list&gt;
						&lt;beans:ref local="httpSessionContextIntegrationFilter" /&gt;
						&lt;beans:ref local="logoutFilter" /&gt;
						&lt;beans:ref local="ssoTicketProcessingFilter" /&gt;
						&lt;beans:ref local="ssoAuthenticationProcessingFilter" /&gt;
						&lt;beans:ref local="exceptionTranslationFilter" /&gt;
						&lt;beans:ref local="filterSecurityInterceptor" /&gt;
					&lt;/beans:list&gt;
				&lt;/beans:entry&gt;
			&lt;/beans:map&gt;
		&lt;/beans:property&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="httpSessionContextIntegrationFilter"
		class="org.springframework.security.context.HttpSessionContextIntegrationFilter"&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="exceptionTranslationFilter"
		class="org.springframework.security.ui.ExceptionTranslationFilter"&gt;
		&lt;beans:property name="authenticationEntryPoint"
			ref="authenticationProcessingFilterEntryPoint"&gt;
		&lt;/beans:property&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="ssoAuthenticationProcessingFilter"
		class="sso.SsoAuthenticationProcessingFilter"&gt;
		&lt;beans:property name="authenticationManager" ref="authenticationManager"&gt;&lt;/beans:property&gt;
		&lt;beans:property name="defaultTargetUrl" value="/index.jsp"&gt;&lt;/beans:property&gt;
		&lt;beans:property name="authenticationFailureUrl" value="/login.jsp"&gt;&lt;/beans:property&gt;
		&lt;beans:property name="invalidateSessionOnSuccessfulAuthentication"&gt;
			&lt;beans:value&gt;true&lt;/beans:value&gt;
		&lt;/beans:property&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="authenticationProcessingFilterEntryPoint"
		class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;
		&lt;beans:property name="loginFormUrl" value="/login.jsp"&gt;&lt;/beans:property&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="filterSecurityInterceptor"
		class="org.springframework.security.intercept.web.FilterSecurityInterceptor"&gt;
		&lt;beans:property name="authenticationManager" ref="authenticationManager" /&gt;
		&lt;beans:property name="accessDecisionManager" ref="accessDecisionManager"&gt;&lt;/beans:property&gt;
		&lt;beans:property name="objectDefinitionSource"&gt;
			&lt;!-- &lt;filter-invocation-definition-source&gt;
				&lt;intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE" /&gt;
				&lt;intercept-url pattern="/secure/**"
					access="ROLE_SUPERVISOR,ROLE_TELLER" /&gt;
			&lt;/filter-invocation-definition-source&gt;
			 --&gt;
			&lt;beans:value&gt;
		        CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                PATTERN_TYPE_APACHE_ANT
                /auth/login.action=ROLE_ANONYMOUS,ROLE_SUPERVISOR,ROLE_TELLER
                /**/*.action=ROLE_SUPERVISOR,ROLE_TELLER
                /**=ROLE_ANONYMOUS,ROLE_SUPERVISOR,ROLE_TELLER
		    &lt;/beans:value&gt;
		&lt;/beans:property&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="accessDecisionManager"
		class="org.springframework.security.vote.AffirmativeBased"&gt;
		&lt;beans:property name="decisionVoters"&gt;
			&lt;beans:list&gt;
				&lt;beans:ref local="voter"&gt;&lt;/beans:ref&gt;
				&lt;beans:bean class="org.springframework.security.vote.AuthenticatedVoter" /&gt;
			&lt;/beans:list&gt;
		&lt;/beans:property&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="voter" class="org.springframework.security.vote.RoleVoter"&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="ssoTicketProcessingFilter" class="sso.SsoTicketProcessingFilter"&gt;
		&lt;custom-filter after="CAS_PROCESSING_FILTER" /&gt;
		&lt;beans:property name="authenticationManager" ref="authenticationManager" /&gt;
		&lt;beans:property name="defaultTargetUrl" value="/index.jsp"&gt;&lt;/beans:property&gt;
		&lt;beans:property name="authenticationFailureUrl" value="/j_spring_security_check"&gt;&lt;/beans:property&gt;
		&lt;beans:property name="invalidateSessionOnSuccessfulAuthentication"&gt;
			&lt;beans:value&gt;true&lt;/beans:value&gt;
		&lt;/beans:property&gt;
	&lt;/beans:bean&gt;
	&lt;beans:bean id="logoutFilter"
		class="org.springframework.security.ui.logout.LogoutFilter"&gt;
		&lt;beans:constructor-arg&gt;
			&lt;beans:value&gt;/login.jsp&lt;/beans:value&gt;
		&lt;/beans:constructor-arg&gt;
		&lt;beans:constructor-arg&gt;
			&lt;beans:list&gt;
				&lt;beans:bean
					class="org.springframework.security.ui.logout.SecurityContextLogoutHandler"&gt;&lt;/beans:bean&gt;
				&lt;beans:bean class="sso.CleanTicketLogoutHandler"&gt;&lt;/beans:bean&gt;
			&lt;/beans:list&gt;
		&lt;/beans:constructor-arg&gt;
	&lt;/beans:bean&gt;
	&lt;authentication-manager alias="authenticationManager" /&gt;
	&lt;beans:bean id="casAuthenticationProvider" class="sso.SsoAuthenticationProvider"&gt;
		&lt;custom-authentication-provider /&gt;
		&lt;beans:property name="userDetailsService" ref="userDetailsService" /&gt;
	&lt;/beans:bean&gt;
	&lt;user-service id="userDetailsService"&gt;
		&lt;user name="rod" password="rod"
			authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" /&gt;
		&lt;user name="dianne" password="dianne" authorities="ROLE_USER,ROLE_TELLER" /&gt;
		&lt;user name="scott" password="scott" authorities="ROLE_USER" /&gt;
		&lt;user name="peter" password="peter" authorities="ROLE_USER" /&gt;
	&lt;/user-service&gt;
&lt;/beans:beans&gt;</pre>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt">&nbsp;</p>
</span>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt">&nbsp;</p>
</span></span>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt">&nbsp;</p>
</p>
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt">&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://ginge.javaeye.com/blog/225778#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</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>Fri, 08 Aug 2008 12:29:46 +0800</pubDate>
        <link>http://ginge.javaeye.com/blog/225778</link>
        <guid>http://ginge.javaeye.com/blog/225778</guid>
      </item>
      <item>
        <title>整理一些常用的正则表达式</title>
        <author>皆 乐</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://zhanglei14616-163-com.javaeye.com">皆 乐</a>&nbsp;
          链接：<a href="http://zhanglei14616-163-com.javaeye.com/blog/225773" style="color:red;">http://zhanglei14616-163-com.javaeye.com/blog/225773</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          正则表达式是一种通用的标准，大部分计算机语言都支持正则表达式，包括as3，这里转摘出了一些常用的正则表达式语句，大家用到的时候就不用自己写了<br /><br />^\d+$　　//匹配非负整数（正整数 + 0）<br />^[0-9]*[1-9][0-9]*$　　//匹配正整数<br />^((-\d+)|(0+))$　　//匹配非正整数（负整数 + 0）<br />^-[0-9]*[1-9][0-9]*$　　//匹配负整数<br />^-?\d+$　　　　//匹配整数<br />^\d+(\.\d+)?$　　//匹配非负浮点数（正浮点数 + 0）<br />^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$　　//匹配正浮点数<br />^((-\d+(\.\d+)?)|(0+(\.0+)?))$　　//匹配非正浮点数（负浮点数 + 0）<br />^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$　　//匹配负浮点数<br />^(-?\d+)(\.\d+)?$　　//匹配浮点数<br />^[A-Za-z]+$　　//匹配由26个英文字母组成的字符串<br />^[A-Z]+$　　//匹配由26个英文字母的大写组成的字符串<br />^[a-z]+$　　//匹配由26个英文字母的小写组成的字符串<br />^[A-Za-z0-9]+$　　//匹配由数字和26个英文字母组成的字符串<br />^\w+$　　//匹配由数字、26个英文字母或者下划线组成的字符串<br />^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$　　　　//匹配email地址<br />^[a-zA-z]+://匹配(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$　　//匹配url<br /><br />匹配中文字符的正则表达式： [\u4e00-\u9fa5]<br />匹配双字节字符(包括汉字在内)：[^\x00-\xff]<br />匹配空行的正则表达式：\n[\s| ]*\r<br />匹配HTML标记的正则表达式：/&lt;(.*)>.*&lt;\/>|&lt;(.*) \/>/<br />匹配首尾空格的正则表达式：(^\s*)|(\s*$)<br />匹配Email地址的正则表达式：\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*<br />匹配网址URL的正则表达式：^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$<br />匹配帐号是否合法(字母开头，允许5-16字节，允许字母数字下划线)：^[a-zA-Z][a-zA-Z0-9_]{4,15}$<br />匹配国内电话号码：(\d{3}-|\d{4}-)?(\d{8}|\d{7})?<br />匹配腾讯QQ号：^[1-9]*[1-9][0-9]*$<br />下表是元字符及其在正则表达式上下文中的行为的一个完整列表：<br />\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个后向引用、或一个八进制转义符。<br />^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的Multiline 属性，^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。<br />$ 匹配输入字符串的结束位置。如果设置了 RegExp 对象的Multiline 属性，$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。<br />* 匹配前面的子表达式零次或多次。<br />+ 匹配前面的子表达式一次或多次。+ 等价于 {1,}。<br />? 匹配前面的子表达式零次或一次。? 等价于 {0,1}。<br />{n} n 是一个非负整数，匹配确定的n 次。<br />{n,} n 是一个非负整数，至少匹配n 次。<br />{n,m} m 和 n 均为非负整数，其中n &lt;= m。最少匹配 n 次且最多匹配 m 次。在逗号和两个数之间不能有空格。<br />? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时，匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串，而默认的贪婪模式则尽可能多的匹配所搜索的字符串。<br />. 匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符，请使用象 ‘[.\n]’ 的模式。<br />(pattern) 匹配pattern 并获取这一匹配。<br />(?:pattern) 匹配pattern 但不获取匹配结果，也就是说这是一个非获取匹配，不进行存储供以后使用。<br />(?=pattern) 正向预查，在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。<br />(?!pattern) 负向预查，与(?=pattern)作用相反<br />x|y 匹配 x 或 y。<br />[xyz] 字符集合。<br />[^xyz] 负值字符集合。<br />[a-z] 字符范围，匹配指定范围内的任意字符。<br />[^a-z] 负值字符范围，匹配任何不在指定范围内的任意字符。<br />\b 匹配一个单词边界，也就是指单词和空格间的位置。<br />\B 匹配非单词边界。<br />\cx 匹配由x指明的控制字符。<br />\d 匹配一个数字字符。等价于 [0-9]。<br />\D 匹配一个非数字字符。等价于 [^0-9]。<br />\f 匹配一个换页符。等价于 \x0c 和 \cL。<br />\n 匹配一个换行符。等价于 \x0a 和 \cJ。<br />\r 匹配一个回车符。等价于 \x0d 和 \cM。<br />\s 匹配任何空白字符，包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。<br />\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。<br />\t 匹配一个制表符。等价于 \x09 和 \cI。<br />\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。<br />\w 匹配包括下划线的任何单词字符。等价于’[A-Za-z0-9_]’。<br />\W 匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]’。<br />\xn 匹配 n，其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。<br />\num 匹配 num，其中num是一个正整数。对所获取的匹配的引用。<br />\n 标识一个八进制转义值或一个后向引用。如果 \n 之前至少 n 个获取的子表达式，则 n 为后向引用。否则，如果 n 为八进制数字 (0-7)，则 n 为一个八进制转义值。<br />\nm 标识一个八进制转义值或一个后向引用。如果 \nm 之前至少有is preceded by at least nm 个获取得子表达式，则 nm 为后向引用。如果 \nm 之前至少有 n 个获取，则 n 为一个后跟文字 m 的后向引用。如果前面的条件都不满足，若 n 和 m 均为八进制数字 (0-7)，则 \nm 将匹配八进制转义值 nm。<br />\nml 如果 n 为八进制数字 (0-3)，且 m 和 l 均为八进制数字 (0-7)，则匹配八
          <br/>
          <span style="color:red;">
            <a href="http://zhanglei14616-163-com.javaeye.com/blog/225773#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/115' target='_blank'><span style="color:red;font-weight:bold;">JavaEye图灵杯第2届问答大赛开始了！8月4日至8月17日，奖品丰厚！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Aug 2008 12:07:01 +0800</pubDate>
        <link>http://zhanglei14616-163-com.javaeye.com/blog/225773</link>
        <guid>http://zhanglei14616-163-com.javaeye.com/blog/225773</guid>
      </item>
      <item>
        <title>一套基于http的聊天c/s结构工具(除了网页tomcat还能做什么)</title>
        <author>Aga</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://aga.javaeye.com">Aga</a>&nbsp;
          链接：<a href="http://aga.javaeye.com/blog/225760" style="color:red;">http://aga.javaeye.com/blog/225760</a>&nbsp;
          发表时间: 2008年08月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在我的认识当中以前一直有一种误区认为：tomcat=web。在我看过了soap协议之后，忽然有了灵感，为什么不能用tomcat来做一个聊天软件的服务器呢？<br />这个具体的设计如下：<br />在http协议中嵌入xml(仿照soap)利用xstream把pojo转化成xml然后在服务器、客户端之间传输。如果考虑扩展性，甚至可以把客户端采用c++或者其他语言来编写，当然，其中的解析http\解析xml会比较痛苦，不知道c++有没有类似commons-httpclient\xstream的开源包。总之目前实现的客户端采用的是swt技术。<br /><br />下面的几个类是一对pojo也就是通讯协议。在客户端、服务器内部使用。需要传输的时候利用xstream转化为xml传输。<br />协议方面主要有2组：heartbeat\upload。<br />HeartBeatRequest就是心跳请求，用于请求下在传送给该用户的消息，HeartBeatResponse就是返回类。uploadRequest是发送给其他用户。UploadResponse就是发送后的反馈。<br />因为http是无状态连接，所以每次请求都需要验证，所以任何XXXRequest都继承自Request类，其中包含验证信息，和发信人地址。protocol的代码就不贴了，太恶心了都是pojo要是想看就自己看代码吧。<br /><br />下面是一个util类，用作把pojo转化为xml并且嵌入到http协议中并发送到固定url并且接受反馈xml并转化为pojo供client端使用具体代码如下：<br /><pre name="code" class="java">

public class MessagePoster&lt;REQ_OBJ, RESP_OBJ> {
	private String url = "http://localhost:8080/HttpChat/Server";

	private String propLoc = "com/cxz/httpchat/util/class.properties";

	// The XStream instance is thread-safe.
	public XStream xstream = null;

	public MessagePoster() {
		initXStream();
	}

	public void setUrl(String url) {
		this.url = url;
	}

	private void initXStream() {
		xstream = new XStream();
		Properties properties = new Properties();
		try {
			properties.load(MessagePoster.class.getClassLoader()
					.getResourceAsStream(propLoc));
			Set keys = properties.keySet();
			for (Object key : keys) {
				xstream.alias((String) key, Class.forName((String) properties
						.get(key)));
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public RESP_OBJ postXML(REQ_OBJ requestObj) throws HttpException,
			IOException {
		RESP_OBJ responseObj = null;
		PostMethod method = null;
		try {
			String xml = xstream.toXML(requestObj);
			HttpClient client = new HttpClient();
			method = new PostMethod(url);
			RequestEntity entity;
			entity = new StringRequestEntity(xml, "text/xml", "utf-8");
			method.setRequestEntity(entity);
			client.executeMethod(method);
			responseObj = (RESP_OBJ) xstream.fromXML(method
					.getResponseBodyAsStream());
		} catch(StreamException e){
			e.printStackTrace();
		} finally {
			method.releaseConnection();
		}
		return responseObj;
	}

}
</pre><br />MessagePoster::initXStream()是初始化xstream的一个函数，具体从配置文件当中读取。具体配置文件如下：<br /><pre name="code" class="properties">HeartBeatRequest=com.cxz.httpchat.message.HeartBeatRequest
HeartBeatResponse=com.cxz.httpchat.message.HeartBeatResponse
UploadRequest=com.cxz.httpchat.message.UploadRequest
UploadResponse=com.cxz.httpchat.message.UploadResponse
Message=com.cxz.httpchat.message.Message
</pre><br />TimedQueue是该项目的另一个核心类，主要是一个消息队列，采用lru算法维护生命周期。<br /><pre name="code" class="java">public class TimedQueue implements Queue&lt;Message> {

	private long lastAccess;

	private int cycleSeconds;
	
	private static final int UNIT_CONVERSION = 1000;

	private Queue&lt;Message> queue = new ConcurrentLinkedQueue&lt;Message>();
	
	public List&lt;Message> toList(){
		resetLastAccess();
		List&lt;Message> messages = new ArrayList&lt;Message>();
		while(!queue.isEmpty()){
			messages.add(queue.remove());
		}
		return messages;
	}

	public boolean isOutOfDate(){
		System.out.println(System.currentTimeMillis() - lastAccess);
		return System.currentTimeMillis() >= lastAccess + cycleSeconds * UNIT_CONVERSION;
	}

	public boolean add(Message e) {
		resetLastAccess();
		queue.add(e);
		return true;
	}

	public Message poll() {
		resetLastAccess();
		return queue.poll();
	}
</pre><br />最后一个核心类就是：SelfCleaner他会定期清理队列中的无用成员，说白了就是长期没有响应的消息。<br /><pre name="code" class="java">package com.cxz.httpchat.util;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.cxz.httpchat.message.Message;
import com.cxz.httpchat.util.TimedQueue;

public class SelfCleaner implements Runnable{

	private Map&lt;Integer, TimedQueue> map = new ConcurrentHashMap&lt;Integer, TimedQueue>();
	
	private Thread gcThread;
	
	private static SelfCleaner instance;
	
	private static final int POLL_SECONDS = 60;
	
	private boolean runFlag = true;
	
	private static final int UNIT_CONVERSION = 1000;
	
	public static synchronized SelfCleaner getInstance() {
		if (instance == null) {
			instance = new SelfCleaner();
		}
		return instance;
	}
	
	private SelfCleaner(){
		
	}
	
	public void stopGc(){
		runFlag = false;
		gcThread.interrupt();
	}
	
	public void forceGc() {
		//Interrupted the sleeping gc Thread
		gcThread.interrupt();
	}
	
	public boolean isStopped() {
		return runFlag;
	}
	
	public void start() {
		gcThread = new Thread(this, "gcThread");
		gcThread.start();
	}
	
	public void run() {
		cleanTheHashMap();
		while(runFlag){
			try {
				Thread.sleep(POLL_SECONDS * UNIT_CONVERSION);
			} catch (InterruptedException e) {
				// This thread has been woken up by a interruption.
				System.out.println("Hey, man, get up from the bed and gc.");
			}
		}
		
	}
	
	public void remove(Integer id) {
		map.remove(id);
	}
	
	public List&lt;Message> getMessage(Integer id){
		if(map.get(id) == null){
			List&lt;Message> list = new ArrayList&lt;Message>();
			return list;
		} else {
			return map.get(id).toList();
		}
	}
	
	public void add(Integer id, Message message) {
		TimedQueue queue = map.get(id);
		if (queue == null) {// If the user does not exists in the queue
			TimedQueue temp = new TimedQueue();
			temp.add(message);
			map.put(id, temp);
		} else {
			queue.add(message);
		}
	}

	private void cleanTheHashMap() {
		Set&lt;Integer> set = map.keySet();
		for (Integer id : set) {
			TimedQueue tq = map.get(id);
			if (tq.isOutOfDate()) {
				map.remove(id);
			}
		}
	}

}
</pre><br />最后一个类就是服务器类，很短，但很精悍。<br /><pre name="code" class="java">package com.cxz.httpchat.server;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.cxz.httpchat.message.HeartBeatRequest;
import com.cxz.httpchat.message.HeartBeatResponse;
import com.cxz.httpchat.message.Message;
import com.cxz.httpchat.message.UploadRequest;
import com.cxz.httpchat.message.UploadResponse;
import com.cxz.httpchat.util.MessagePoster;
import com.cxz.httpchat.util.SelfCleaner;
import com.thoughtworks.xstream.XStream;

/**
 * Servlet implementation class for Servlet: Server
 *
 */
public class Server extends javax.servlet.http.HttpServlet implements
		javax.servlet.Servlet {
	/* (non-Java-doc)
	 * @see javax.servlet.http.HttpServlet#HttpServlet()
	 */
	private XStream xstream = new MessagePoster().xstream;
	
	SelfCleaner map = SelfCleaner.getInstance();

	public Server() {
		super();
		map.start();
	}

	
	/* (non-Java-doc)
	 * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
	}

	/* (non-Java-doc)
	 * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		Object req = xstream.fromXML(request.getInputStream());
		Object resp = processRequest(req);
		if(resp != null){
			xstream.toXML(resp, response.getOutputStream());
		} else {// Check the null
			
		}
	}
	
	private Object processRequest(Object req){
		if(req.getClass() == UploadRequest.class){
			UploadRequest upload = (UploadRequest)req;
			return processUpload(upload);
		} else if (req.getClass() == HeartBeatRequest.class) {
			HeartBeatRequest heartBeat = (HeartBeatRequest)req;
			return processHeartBeat(heartBeat);
		} else {
			return null;
		}
	}

	/**
	 * Just for fun
	 * @param upload
	 * @return
	 */
	private UploadResponse processUpload(UploadRequest upload) {
		//Just for tracking the message flow
		Message message = new Message();
		message.setFrom(upload.getId());
		message.setDate(upload.getDate());
		message.setContent(upload.getContent());
		map.add(upload.getTo(), message);
		UploadResponse resp = null;
		if(true/*upload.getId().equals(new Integer(1))&&upload.getPwd().equals("19841230")*/){
			resp = new UploadResponse(); 
			resp.set