Profiel van 小羊羊羊的圈Foto'sWeblogLijsten Extra Help

Weblog


    30-3-2007

    好累啊~

          最近工作太累啊,好累,驴一样地劳作着,走路的感觉都象在飘,左右都不分了。
          一肚子的委屈,不开心不开心……
         
          blessing for the project ,blessing for me and blessing for my being back to Beijing!
    28-2-2007

    软件开发?!

         C.A.R Hoare为代表的数学观
                   + 以Bertrand Meyer为代表的工程观
                              + 以很多程序员为代表的手工艺观
                                                         =软件开发!
     
        哦,额滴神,原来额正揣着额的数学头脑,身着蓝色工人制服,进行着一项手工艺制造活动……
    21-7-2006

    Liferay学习(1)

          因为要接手的项目需要portal技术,于是断断续续看了几天的Liferay框架,框架层次很清晰,但是代码太多了,加上“夏日炎炎正好眠”,于是每每看着看着就会引发无数的瞌睡虫,效率也较低(画外音:找这么多借口,还不就懒呗)。初步整理了一份关于Liferay平台中,开发一个最简单的porlet需要涉及到的类及相关配置文件图,用偶不太娴熟的UML语言描述出来了,或许有很多的不完善甚至错误的地方(比如没有把本地化文件列出来),呵呵,待俺发现慢慢更正,烦走过路过的赐教。
         
     
    clip_image001_0007.gif
    28-4-2006

    一种优雅的流行架构:SSH

     
    clip_image001_0007.gif

    应用层

          许多设计良好的web 应用,可以被按职责分为四层。这些层次是表现层、持久层、业务层、和领域模型层。每一个层次都有其独特的职责,不能把各自的功能与其它层次相混合。每一个应用层都应该和其它层隔离开来,但允许使用接口在层间进行通信。我们开始来看看每个层,并讨论一下它们各自都应该提供什么和不应该提供什么。

     

    表现层

          一个典型的web 应用的末端是表现层。许多Java 开发者都知道Struts 提供了什么东西。然而,太多时候,耦合代码比如业务逻辑被放进org.apache.struts.Action中。所以,我们先总结一下Struts 之类的框架应该提供什么。下面就是Struts 的职责所在:

    • 管理用户的请求和响应
    • 提供一个控制起来将调用委托到业务逻辑和其他上游处理
    • 将来自于抛出例外的其他层的例外处理到Struts Action 中
    • 组装可以在视图中表现的模型对象
    • 执行UI 校验

    下面是一些经常可以使用Struts进行编码但是不应该和表现层关联的事情:

    • 直接和数据库交互,比如JDBC 调用
    • 与应用相关的业务逻辑和校验
    • 事务管理

    在表现层中引入这些类型的代码将导致类型耦合和维护负担。

     

    持久层

          一个典型Web应用的另一端是持久层。这也是应用中最容易很快失控的地方。开发者通常低估了自己构建自己的持久层框架的挑战。一个定制的,内部开发的持久层不仅需要大量的开发时间,并且通常缺乏功能和难以管理。目前有许多解决这些问题的开源对象关系映射 (ORM) 框架。特别地, Hibernate 框架就允许Java中的对象-关系的持久性和查询服务。Hibernate 对已经熟悉了SQL 和JDBC API 的Java开发者来或具有中度的学习曲线。Hibernate 的持久对象基于POJO和Java 群集(collections)。此外,使用Hibernate 不和你的IDE接口。下面列出了你需要在持久性框架中编写的代码类型:

    • 查询关系信息到对象中。Hibernate 是通过称为HQL的OO查询语言,或者使用更有表现能力的规则API,来完成这个工作的。除了使用对象而不是表,使用字段而不是列的方式,HQL非常类似于 SQL。也有一些新的特定的HQL 语言特征需要学习;但是,它们是很容易理解和良好编写的。HQL 是一种用于查询对象的自然语言,而对象,只需要很少的学习曲线吧。.
    • 存储、更新和删除存储在数据库中的信息
    • 高级的对象关系映射框架比如Hibernate支持大部分主流SQL数据库,它们支持父/子关系,事务,继承和多态。

    下面是应该在持久层避免的一些事情:

    • 业务逻辑应该置于应用的更高层中。这里只允许数据访问方法。
    • 不应该使持久逻辑和表现逻辑耦合。避免表现组件如JSP或者基于servlet的类中的逻辑直接和数据访问进行通信。通过将持久性逻辑隔离在其自己的层中,应用将具有更加灵活的修改性而不影响到其他层的代码。例如, Hibernate 可以使用其他持久框架和API代替,而不需要修改其它层中的代码。

    业务层

          典型的Web应用的中间组件一般是业务层和服务层。从编程的角度来说,service layer经常被忽略。这种类型的代码散布于UI表现层和持久层并不是不多见。这些都不是正确的地方因为它导致了紧密耦合的应用和难以维护的代码。幸运的是,大多数框架都解决了这个问题。这个空间内最流行的两个框架是Spring 和PicoContainer。它们都被视为是具有非常小的足迹(footprint)并且决定如何将你的对象整合在一起的微容器(microcontainer)。这些框架都建立在一种叫做依赖性注入(dependency injection) (也称控制反转(inversion of control:IOC))的简单概念之上。我们将关注Spring中通过针对命名配置参数的bean属性的setter 注入的使用。Spring 也允许一种更加高级的构造器注入(constructor injection)形式作为setter injection 的可选替代。对象通过简单的XML 文件进行连接,该配置文件包含对各种对象的引用,比如事务管理处理器(transaction management handler),对象工厂,包含业务逻辑的服务对象,以及数据访问对象(DAO)。

    我们随后会用一些例子来澄清Spring中使用这些改变的方式。

    业务层应该负责下面的问题:

    • 处理应用的业务逻辑和业务校验
    • 管理事务
    • 允许与其他层进行交互的接口
    • 管理业务级对象之间的依赖性
    • 加入了表现和持久层之间的灵活性,以便它们不需要彼此进行直接通信
    • 从表现层暴露上下文给业务层以获得业务服务
    • 管理从业务层到表现层的实现

    领域模型层

          最后,因为我们要解决实际的问题的web应用,我们需要一套在不同的层间移动的对象。领域模型层包含的是表达实际业务对象的对象,比如Order, OrderLineItem, Product 等等。这一层允许能让开发者不再构建和维护不必要的数据传输对象DTO来匹配其领域对象。例如, Hibernate允许你读取数据库信息到一个领域对象的对象图中,以便你可以在离线的情况下将其表现在UI层中。这些对象可以被更新并跨过表现层发送回去,然后进行数据库更新。另外,你不再需要将对象转变成DTO,因为它们在不同的层间移动时可能会丢失事务。这种模型允许Java 开发者能够以OO风格的方式很自然的处理对象,而不用编写额外的代码。

    27-1-2006

    代码重构

         【摘自“JBuilder 2005 代码重构深度探索”一文及“重构”一书】
     
         “任何一个傻瓜都能写出计算机可以理解的程序,只有写出人类容易理解的程序才是优秀的程序员。”
                                                               ————Martin Flower《重构--改善既有代码的设计》
     
         什么是重构呢?重构就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。
     
         也许有人会问,为什么不在项目开始时多花些时间把设计做好,而要以后花时间来重构呢?要知道一个完美得可以预见未来任何变化的设计,或一个灵活得可以容纳任何扩展的设计是不存在的。系统设计人员对即将着手的项目往往只能从大方向予以把控,而无法知道每个细枝末节,其次永远不变的就是变化,提出需求的用户往往要在软件成型后,始才开始"品头论足",系统设计人员毕竟不是先知先觉的神仙,功能的变化导致设计的调整再所难免。所以"测试为先,持续重构"作为良好开发习惯被越来越多的人所采纳,测试和重构像黄河的护堤,成为保证软件质量的法宝。
     
          通过重构可以达到以下的目标:
          ·  持续偏纠和改进软件设计
          ·  使代码更易为人所理解
          ·  帮助发现隐藏的代码缺陷
          ·  从长远来看,有助于提高编程效率
     
          需要代码重构的强烈信号:
          ·  代码中存在重复的代码
          ·  过大的类和过长的方法
          ·  牵一毛而需要动全身的修改
          ·  类之间需要过多的通讯
          ·  过度耦合的信息链
          ·  各立山头干革命(有两个类或两个方法虽然命名不同但却拥有相似或相同的功能)
          ·  不完美的设计
          ·  缺少必要的注释
     
          重构的每个步骤都很简单,甚至简单过了头,你只需要把某个值域(field)从一个class移到另一个class,把某些代码从一个函数(method)拉出来构成另一个函数,或是在class hierarchy中把某些代码推上推下就行了。但是,聚沙成塔,这些小小的修改累积起来就可以根本改善设计质量。
         
          通过重构(refactoring),我们可以找出改变的平衡点。我们会发现所谓设计不再是一切动作的前提,而是在整个开发过程中逐渐浮现出来。在系统构筑过程中,我们可以学习如何强化设计;其间带来的互动可以让一个程序在开发过程中持续保有良好的设计。



     
    27-10-2005

    借来几个javascript校验函数

    几个javascript校验函数

    /*

    -------------- 函数检索 --------------
    trim函数:                                       trim() lTrim() rTrim()
    校验字符串是否为空:                 checkIsNotEmpty(str)
    校验字符串是否为整型:             checkIsInteger(str)
    校验整型最小值:                         checkIntegerMinValue(str,val)
    校验整型最大值:                         checkIntegerMaxValue(str,val)
    校验整型是否为非负数:             isNotNegativeInteger(str)
    校验字符串是否为浮点型:         checkIsDouble(str)
    校验浮点型最小值:                     checkDoubleMinValue(str,val)
    校验浮点型最大值:                     checkDoubleMaxValue(str,val)
    校验浮点型是否为非负数:         isNotNegativeDouble(str)
    校验字符串是否为日期型:         checkIsValidDate(str)
    校验两个日期的先后:                 checkDateEarlier(strStart,strEnd)
    校验字符串是否为email型:         checkEmail(str)

    校验字符串是否为中文:             checkIsChinese(str)
    计算字符串的长度,一个汉字两个字符:   realLength()
    校验字符串是否符合自定义正则表达式:   checkMask(str,pat)
    得到文件的后缀名:                     getFilePostfix(oFile) 
    -------------- 函数检索 --------------
    */

    /**
    * added by LxcJie 2004.6.25
    * 去除多余空格函数
    * trim:去除两边空格 lTrim:去除左空格 rTrim: 去除右空格
    * 用法:
    *     var str = "  hello ";
    *     str = str.trim();
    */
    String.prototype.trim = function()
    {
        return this.replace(/(^[\s]*)|([\s]*$)/g, "");
    }
    String.prototype.lTrim = function()
    {
        return this.replace(/(^[\s]*)/g, "");
    }
    String.prototype.rTrim = function()
    {
        return this.replace(/([\s]*$)/g, "");
    }


    /************ Empty ****************/
    /**
    *校验字符串是否为空
    *返回值:
    *如果不为空,定义校验通过,返回true
    *如果为空,校验不通过,返回false              

    * 参考提示信息:输入域不能为空!
    */
    function checkIsNotEmpty(str)
    {
        if(str.trim() == "")
            return false;
        else
            return true;
    }//~~~
    /*----------------- Empty ----------------------------------*/


    /************ Integer **********************/
    /**
    *校验字符串是否为整型
    *返回值:
    *如果为空,定义校验通过,返回true
    *如果字串全部为数字,校验通过,返回true
    *如果校验不通过,返回false    

    *参考提示信息:输入域必须为数字!
    */
    function checkIsInteger(str)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        if(/^(\-?)(\d+)$/.test(str))
            return true;
        else
            return false;
    }//~~~


    /**
    *校验整型最小值
    *str:要校验的串。  val:比较的值
    *返回值:
    *如果为空,定义校验通过,返回true
    *如果满足条件,大于等于给定值,校验通过,返回true
    *如果小于给定值,返回false             
    *参考提示信息:输入域不能小于给定值!
    */
    function checkIntegerMinValue(str,val)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        if(typeof(val) != "string")
            val = val + "";
        if(checkIsInteger(str) == true)
        {
            if(parseInt(str,10)>=parseInt(val,10))
                return true;
            else
                return false;
        }
        else
            return false;
    }//~~~


    /**
    *校验整型最大值
    *str:要校验的串。  val:比较的值
    *返回值:
    *如果为空,定义校验通过,返回true 
    * 如果满足条件,小于等于给定值,校验通过,返回true  
    *如果大于给定值,返回false  
    * 参考提示信息:输入值不能大于给定值!
    */
    function checkIntegerMaxValue(str,val)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        if(typeof(val) != "string")
            val = val + "";
        if(checkIsInteger(str) == true)
        {
            if(parseInt(str,10)<=parseInt(val,10))
                return true;
            else
                return false;
        }
        else
            return false;
    }//~~~


    /**
    *校验整型是否为非负数
    *str:要校验的串。
    *返回值:
    *如果为空,定义校验通过,返回true
    *如果非负数,返回true
    *如果是负数,返回false              
    *参考提示信息:输入值不能是负数!
    */
    function isNotNegativeInteger(str)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        if(checkIsInteger(str) == true)
        {
            if(parseInt(str,10) < 0)
                return false;
            else
                return true;
        }
        else
            return false;
    }//~~~
    /*------ Integer --------------*/


    /***************** Double *************/
    /**
    *校验字符串是否为浮点型
    *返回值:
    *如果为空,定义校验通过,返回true
    *如果字串为浮点型,校验通过,返回true
    *如果校验不通过,返回false    

    * 参考提示信息:输入域不是合法的浮点数!
    */
    function checkIsDouble(str)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        //如果是整数,则校验整数的有效性
        if(str.indexOf(".") == -1)
        {
            if(checkIsInteger(str) == true)
                return true;
            else
                return false;
        }
        else
        {
            if(/^(\-?)(\d+)(.{1})(\d+)$/g.test(str))
                return true;
            else
                return false;
        }
    }//~~~


    /**
    *校验浮点型最小值
    *str:要校验的串。  val:比较的值
    *返回值:
    *如果为空,定义校验通过, 返回true
    *如果满足条件,大于等于给定值,校验通过,返回true
    *如果小于给定值, 返回false             
    *参考提示信息:输入域不能小于给定值!
    */
    function checkDoubleMinValue(str,val)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        if(typeof(val) != "string")
            val = val + "";
        if(checkIsDouble(str) == true)
        {
            if(parseFloat(str)>=parseFloat(val))
                return true;
            else
                return false;
        }
        else
            return false;
    }//~~~


    /**
    *校验浮点型最大值
    *str:要校验的串。  val:比较的值*返回值:
    *如果为空,定义校验通过,返回true
    *如果满足条件,小于等于给定值,校验通过,返回true
    *如果大于给定值, 返回false    
    *参考提示信息:输入值不能大于给定值!
    */
    function checkDoubleMaxValue(str,val)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        if(typeof(val) != "string")
            val = val + "";
        if(checkIsDouble(str) == true)
        {
            if(parseFloat(str)<=parseFloat(val))
                return true;
            else
                return false;
        }
        else
            return false;
    }//~~~


    /**
    *校验浮点型是否为非负数
    *str:要校验的串。
    *返回值:
    *如果为空,定义校验通过,返回true
    *如果非负数,返回true
    *如果是负数,返回false              
    *参考提示信息:输入值不能是负数!
    */
    function isNotNegativeDouble(str)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        if(checkIsDouble(str) == true)
        {
            if(parseFloat(str) < 0)
                return false;
            else
                return true;
        }
        else
            return false;
    }//~~~
    /*---------- Double ---------------------------*/


    /*********date ********************************/
    /**
    *校验字符串是否为日期型
    *返回值:
    *如果为空,定义校验通过, 返回true
    *如果字串为日期型,校验通过,  返回true
    *如果日期不合法,返回false  
    *参考提示信息:输入域的时间不合法!(yyyy-MM-dd)
    */
    function checkIsValidDate(str)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        var pattern = /^((\d{4})|(\d{2}))-(\d{1,2})-(\d{1,2})$/g;
        if(!pattern.test(str))
            return false;
        var arrDate = str.split("-");
        if(parseInt(arrDate[0],10) < 100)
            arrDate[0] = 2000 + parseInt(arrDate[0],10) + "";
        var date =  new Date(arrDate[0],(parseInt(arrDate[1],10) -1)+"",arrDate[2]);
        if(date.getYear() == arrDate[0]
           && date.getMonth() == (parseInt(arrDate[1],10) -1)+""
           && date.getDate() == arrDate[2])
            return true;
        else
            return false;
    }//~~~


    /**
    *校验两个日期的先后
    *返回值:
    *如果其中有一个日期为空,校验通过,    返回true
    *如果起始日期早于等于终止日期,校验通过,返回true
    *如果起始日期晚于终止日期,返回false 
    * 参考提示信息: 起始日期不能晚于结束日期。
    */
    function checkDateEarlier(strStart,strEnd)
    {
        if(checkIsValidDate(strStart) == false || checkIsValidDate(strEnd) == false)
            return false;
        //如果有一个输入为空,则通过检验
        if (( strStart == "" ) || ( strEnd == "" ))
            return true;
        var arr1 = strStart.split("-");
        var arr2 = strEnd.split("-");
        var date1 = new Date(arr1[0],parseInt(arr1[1].replace(/^0/,""),10) - 1,arr1[2]);
        var date2 = new Date(arr2[0],parseInt(arr2[1].replace(/^0/,""),10) - 1,arr2[2]);
        if(arr1[1].length == 1)
            arr1[1] = "0" + arr1[1];
        if(arr1[2].length == 1)
            arr1[2] = "0" + arr1[2];
        if(arr2[1].length == 1)
            arr2[1] = "0" + arr2[1];
        if(arr2[2].length == 1)
            arr2[2]="0" + arr2[2];
        var d1 = arr1[0] + arr1[1] + arr1[2];
        var d2 = arr2[0] + arr2[1] + arr2[2];
        if(parseInt(d1,10) > parseInt(d2,10))
           return false;
        else
           return true;
    }//~~~
    /*----------------------- date ----------------------------------*/


    /******************* email ********************/
    /**
    *校验字符串是否为email型
    *返回值:
    *如果为空,定义校验通过,返回true
    *如果字串为email型,校验通过,返回true
    *如果email不合法, 返回false   
    *参考提示信息:Email的格式不正確!
    */
    function checkEmail(str)
    {
        //如果为空,则通过校验
        if(str == "")
            return true;
        if (str.charAt(0) == "." || str.charAt(0) == "@" || str.indexOf(
    '@', 0) == -1
            || str.indexOf('.', 0) == -1 || str.lastIndexOf("@") == str.length-1 || str.lastIndexOf(".") == str.length-1)
            return false;
        else
            return true;
    }//~~~
    /*------------------- email ------------------------------*/


    /********************* chinese ****************/
    /**
    *校验字符串是否为中文
    *返回值:
    *如果为空,定义校验通过,返回true
    *如果字串为中文,校验通过,返回true
    *如果字串为非中文,返回false   
    *参考提示信息:必须为中文!
    */
    function checkIsChinese(str)
    {
        //如果值为空,通过校验
        if (str == "")
            return true;
        var pattern = /^([\u4E00-\u9FA5]|[\uFE30-\uFFA0])*$/gi;
        if (pattern.test(str))
            return true;
        else
            return false;
    }//~~~


    /**
    * 计算字符串的长度,一个汉字两个字符
    */
    String.prototype.realLength = function()
    {
      return this.replace(/[^\x00-\xff]/g,"**").length;
    }
    /*------------------- chinese -------------------------------*/


    /******************* mask ***********************/
    /**
    *校验字符串是否符合自定义正则表达式
    *str 要校验的字串  pat 自定义的正则表达式
    *返回值:
    *如果为空,定义校验通过,返回true
    *如果字串符合,校验通过, 返回true
    *如果字串不符合, 返回false   
    *参考提示信息:必须满足***模式
    */
    function checkMask(str,pat)
    {
        //如果值为空,通过校验
        if (str == "")
            return true;
        var pattern = new RegExp(pat,"gi")
        if (pattern.test(str))
            return true;
        else
            return false;
    }//~~~
    /*---------------------- mask ---------------------------------*/


    /************************* file *********************/
    /**
    * added by LxcJie 2004.6.25
    * 得到文件的后缀名
    * oFile为file控件对象
    */
    function getFilePostfix(oFile)
    {
        if(oFile == null)
            return null;
        var pattern = /(.*)\.(.*)$/gi;
        if(typeof(oFile) == "object")
        {
            if(oFile.value == null || oFile.value == "")
                return null;
            var arr = pattern.exec(oFile.value);
            return RegExp.$2;
        }
        else if(typeof(oFile) == "string")
        {
            var arr = pattern.exec(oFile);
            return RegExp.$2;
        }
        else
            return null;
    }//~~~
    /*----------------------- file ------------------------------*/

    window.close关闭窗口,如何可以不弹出系统提示,直接关闭
            private void btnClose_Click(object sender, System.EventArgs e)
            {
                Response.Write("<script language=javascript>window.opener=null;window.close() ;</script>");
            }
    如果是通过子窗体关闭父窗体时怎么做呢, 子窗体(弹出窗体):
                Response.Write("<script>window.opener.top.opener=null;window.opener.top.close()</script>") 

    15-7-2005

    Applet 不能访问本地文件系统的问题

    因为applet是运行在浏览器端的小程序,所以存在比较多的安全限制,比如说不能访问本地的文件系统,怎么办呢?——不怕,有servlet呢!
     
    只需要两个步骤:
     
    1、在servlet中读入文件流,通过doPost()方法可以传递Object。
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ObjectOutputStream outputToApplet;
        try {
          outputToApplet = new ObjectOutputStream(response.getOutputStream());
          System.out.println("Sending Files vector to applet...");
          outputToApplet.writeObject(getFiles());//getFiles()方法读入一系列文件
          outputToApplet.flush();
          outputToApplet.close();
          System.out.println("Data transmission complete.");
        }
        catch (IOException e) {
          e.printStackTrace();
        }
      }
     
    2、在applet中,从servlet流中读出传送的object。
     
        private static final String servletLocation = "http://localhost:8088/secnms/GetFileNames";
       
        URL url = new URL(servletLocation);
        URLConnection servletConnection = url.openConnection();
     
        // inform the connection that we will send output and accept input
        servletConnection.setDoInput(true);
        servletConnection.setDoOutput(true);
     
        // Don't used a cached version of URL connection.
        servletConnection.setUseCaches (false);
        servletConnection.setDefaultUseCaches(false);
     
        // Specify the content type that we will send binary data
        servletConnection.setRequestProperty("Content-Type","application/octet-stream");
     
        // send the student object to the servlet using serialization
        ObjectOutputStream outputToServlet = new ObjectOutputStream(servletConnection.getOutputStream());
     
        // serialize the object
        outputToServlet.writeObject("dummy");
        outputToServlet.flush();
        outputToServlet.close();
     
        // Read the input from the servlet.
        // The servlet will return a serialized vector containing student entries.
        ObjectInputStream inputFromServlet = new ObjectInputStream(servletConnection.getInputStream());
        Object fl = inputFromServlet.readObject();
        ……
     
    这样,只要在tomcat之类服务上让servlet跑起来,一切就搞定了。
     
    很简单的东西,只是以前没用到,所以记下来。
    从细节开始,踏踏实实地积累——小羊,不要再浮躁啦!!!
     
    5-7-2005

    与数据库无关的多页表格排序(前两天折腾我的那个问题)

    前两天要写一个给多页表格排序的工具类,想过去好像是挺简单的,但是由于本人对api的不熟悉,它着实地折腾了我一番,走了些弯路,犯了些白痴错误,但在这过程中,我又学到了些以前没接触过的东西,所以想记下来。
    (昨天牺牲了一个中午的休息事件,写了长篇大论总结了一番,结果在提交的时候不知道出了什么问题,全不见了,我那个哭啊那个恨啊,无奈,天杀的破网,心痛ing~~~,现在重新总结一下,把要点提提吧。)
    (友情提示:以后写日志,一定要先copy后再提交,要不~~~血的教训啊!!!)
     
    响应表格头点击事件,根据被点击的列,根据列值大小来给一个多页表格排序。
    表格跟数据库无关,我将表格数据记录在一个Vector中,Vector的每一项是一个实物对象,对应表格的每行,其中一个cell显示对象的一个属性。排序,实际上就是根据vector中若干实物对象的某个指定属性值的大小顺序,在vector中给这些实物对象重新排列顺序,之后在table中显示出来。
     
    写了个排序的类MultPageTableSortManager,实现TableModelListener接口,在程序使用的时候只需要加一句话(new MultPageTableSortManager(jTable, vector),根据构造参数不同,还可以指定根据某列或者某几列进行排序)就可以了,相对方便。
    下面介绍一下主要方法:
     
    1、addMouseListener(final JTable table):对JTable添加鼠标时间侦听,获得用户想排序的某列。
         该方法给table的tableHeader加上MouseListener,响应mouseClicked事件,在这事件中获取鼠标点击的某列,并判断此时该是升序排列还是降序排列(初始默认升序,点击后换为降序,再次点击又为升序,如此交替),之后判断改列是否可排序,若可以,调用排序方法根据其进行排序。
         获取鼠标点击的某列(从0开始):
         int point = table.columnAtPoint(mouseevent.getPoint());
         int colIndex = table.convertColumnIndexToModel(point);
     
    2、sort():主要排序的方法。
         主要是调用了Arrays.sort(Object[] obj)方法。这里的obj的每个元素必须是一个实现了Comparable接口的类(我是这样定义这个类的:把这个类作为当前MultPageTableSortManager类的一个内部类,主要包含要排序的对象以及相应序号index)。
         在调用Arrays.sort(Object[] obj)方法的时候,系统实际上是调用了Comparable接口中的compareTo(Object obj)方法来觉定相比较的两个对象的大小。
        在具体比较的时候,对于一般的简单对象特别是String对象,可以利用java.text.Collator中的compare(Object,Object)方法,这还能解决中文的排序问题:
        java.text.Collator cnCollator = java.text.Collator.getInstance(Locale. getDefault());
        ……
        return cnCollator.compare(obj1, obj2);//0-obj1与obj2大小相等;
                                                             //1-obj1大于obj2;
                                                            //-1-obj1较obj2小;
                                                            //若为降序排列则为cnCollator.compare(obj2, obj1);
     
    3、存在的问题。
         不知道是不是在加监听器的时候存在一些问题。我的程序有对vector中数据进行查询筛选后再显示的功能。比如原来的vector大小为 100,筛选后为30。那么排序是对这30条数据进行构造MultPageTableSortManager进行排序。但是由debug调试跟踪的结果看出,每响应一次鼠标点击事件,程序会自己调用三四次addMouseListener(final JTable table)方法,并且第一次调用的时候,vector会恢复到原来100的大小,之后的调用中vector又恢复到30的大小,这个现象太奇怪了!!!但是如果不debug,程序执行是无误的。所以这个问题被我暂时搁置了,等有空的时候再找原因吧。
     
    4-7-2005

    关于.tar.gz文件的解压

    在linux或unix上,文件压缩大多是.tar.gz的格式的,怎样在java程序中将其解压呢?
    感谢Justin同学的大力帮忙,有个开源的项目com.ice.tar(http://www.gjt.org/pkgdoc/com/ice/tar/index.html)提供了些api,应该能利用其中提供的方法解决这类的一些问题。
     
    啊,我哭,利用com.ice.tar提供的api解决tar文件的压缩解压问题,在调用TarInputStream(构造的时候,用的是FileInputStream)的getNextEntry()方法时,报如下错误:“bad header in block 0 record 0, header magic is not 'ustar' or unix-style zeros, it is '-10025-376-2122-82', or (dec) -100, 25, -37, 6, -2, 122, -82”。难道com.ice.tar提供的方法只能解决unix下tar文件的压缩解压问题?
     
    呀,笨笨,其实在java.util.zip包中就有提供压缩/解压gz格式文件的方法。主要用到GZIPOutputStream/GZIPInputStream类。写了个解压的例子,发现因为GZIPInputStream类就提供了read(byte[] buf, int off, int len)方法来读取解压后的输入流,所以针对多个文件压缩在一个包下或多个包下或还有包的路径层次问题,它全不理会,GZIPInputStream好像无法记录原来的目录结构层次,而是将所有压缩包下的文件内容都读到一个文件中。
    怎样保持原来的目录结构呢?
     
    或许通过把java.util.zip.GzipInputStream应用到Ice Tar API中,tar.gz文件就可以很容易的读取了,这样上面的问题或许也就解决了,有空试试。
     
     
    1-7-2005

    bjug

    前两周在徐×的引见下,加入了bjug——beijing java user group,挺喜欢那的,一个关于技术的民间组织,在那,我迷失在一群牛人中~~小羊,努力学习、努力进步!
     
    不过最近组织里有不好的消息,我们中的一员王俊(后山),不幸得了骨髓增生,需要好多好多的钱治病。俗话说一方有难,八方相助,bjug里的朋友们正积极的想办法,从组织捐款、设立专门网页、号召大家从msn或论坛等途径征集社会募捐等,连专门的图标都有,一切进行的井然有序,一切都是大家自发自愿的,目的只有一个,让王俊这样一个优秀的程序员早日恢复,恢复健康,恢复本该属于他的快乐~
    为他祈祷祝福!
    (有关网址:http://befresh.bjug.org
     
    珍惜自己的健康!
    30-6-2005

    小样儿,这折腾了我三天的难题:多页表格JTable排序(数据库无关)

    两天了,遇到一个难题一直没解决,越来越发现自己对java的了解还非常欠缺,很多api都不熟悉,不过不怕,我喜欢研究问题,寻找解决方法,虽然总是绕了很多弯路,呵呵,没办法,小羊笨笨啦~
    记下遇到的问题吧:
          要对多页的JTable实现点击tableheader排序,我把显示的数据都记录在了vector里了,其中没一项都是一个日志信息对象,现在想写个通用类来实现排序的功能,可是两天的研究结果是——————混乱,不知道为什么给tableheader添加的MouseListener监听器,当点击tableheader的时候,会四次响应(也就是调用)这个监听处理方法呢???而且每次vector都在变,太奇怪了,是java api的bug么???这是参考网上下载的人家对单页table实现排序的方法。搞不懂,继续想ing~~
    糟糕,今天好像都在这写日志了,嘻嘻~
     
    *