服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

利用jakarta commons lang简化java

摘要
  作为企业java开发人员,我们总是需要实现各种功能,如解析xml、使用http、验证输入以及处理日期等。使用jakarta commons项目的目的在于创建负责处理所有此类常用任务的组件,从而节约时间,让您集中精力处理核心业务解决方案。在本文中,我们将对jakarta commons 项目作简单介绍,然后演示如何使用jakarta commons内的lang组件来处理和简化日常java任务,比如字符串操作、使用日期和日历、比较数据对象以及对象排序等。对于本文中的所有例子,我们都将使用最新的lang版本(即2.1版本)。
  编者按:本文中的代码是根据commons lang的 rc1版本 编写的。其最终版不久将发行。

commons和lang组件简介
  jakarta commons 项目旨在实现可重用的 java 组件。此项目包含数十个组件,用以简化 java 的开发,每个组件负责满足一个特定需求。有大量的组件可用,且不仅限于在特定类型的java应用程序中使用。

项目分类在两个部件中:

  1. commons proper:commons proper中的项目已可以投入实际使用。
  2. commons sandbox:sandbox内的项目仍然处于实验阶段。

  目前commons proper中有33个项目,commons sandbox中有22个项目,故而,任何一类java项目都有其存在的意义。
  lang组件是jakarta commons中较为流行的组件之一。lang是要呈现在j2se本身中的一组类。
  在本文中,我们将了解lang最有用的一些功能。要注意的是,也可以只使用基本java类来完成lang的每个功能,但相对于自己学习、理解并编写代码而言,使用lang要简单得多。即使您能够写出最好的代码,使用经过实验和测试的lang的功能会更快一些,能节省大量的检查与测试时间。随着lang一起提供了特有的junit测试用例,lang的使用极其广泛,已经历了其创建者和现实世界的种种考验。
  lang的一个重要特征是其简单性。通常来说,新的java组件十分复杂,要了解若干种技术才能使用这些组件。要理解组件的用途通常都很难,更别说实际使用该组件了。但对于大多数commons组件而言,这就不是问题了。lang一类组件使用方便,无论对java初学者还是高级java用户都非常有用。
  如果在采用技术前需要有十足的保证,则请在保存您的java软件的目录中搜索commons-lang*.jar。结果会让您感到很意外。 tomcatstrutshibernatespringwebwork 等常见java项目都使用lang。
  首先让我们看一看使用lang进行大多数开发人员几乎每天必须进行的操作――字符串操作。

处理字符串
  无论应用程序是基于swing、j2ee或j2me的,它都必须使用字符串。所以,尽管在java中使用字符串相当简单,但是如果希望按照一定的条件修改和处理字符串,事情就不那么简单了。您不得不在各种与字符串相关的类中寻找各种不常用的方法,然后想办法使其协同工作,以获得所需的结果。虽然有些lang方法与j2se中的某些方法重叠,但在大多数情况下,一个lang方法就可提供各种类中的多个j2se方法的功能,从而帮助您获得所需的输出。
  lang组件有许多专门用于字符串操作的类。现在我们将使用一个简单的java应用程序来演示一些较为有用的类和方法。
  当应用程序接受用户输入时,由于用户可能会存在输入错误的情况,或用户可能以各种格式输入数据,而您希望只采用一种格式进行处理和存储,则通常会涉及到对字符串进行操作。
  例如,您有一个带输入框的窗体,用户在此输入框内输入许可证密钥。您希望允许输入1111-java格式的密钥。您必须进行以下操作:

  1. 检查是否为空字符串。
  2. 忽略空格。
  3. 密钥区分大小写。
  4. 用“-”标记分隔密钥字符串,然后检查第一部分是否全部是数字,第二部分包含的字符是否只来自有效字符集“j”、“a”、“v”、“a”。
  5. 两个部分均应有四个字符。
  6. 第一部分的第四个数字应该是“0”。

  只有当密钥满足所有这些条件时,应用程序才会查询数据库,检查该密钥是否合法。
  如果不花大量的时间浏览string、stringtokenizer和其他类的api文档,您能完成以上的任务么?我不能,所以现在我将试着用lang组件来管理验证。
清单1. checklicensekey()方法

/** * check if the key is valid * @param key license key value * @return true if key is valid, false otherwise. */public static boolean checklicensekey(string key){    //checks if empty or null    if(stringutils.isblank(key)){        return false;    }    //delete all white space    key= stringutils.deletewhitespace(key);    //split string using the - separator    string[] keysplit = stringutils.split(key, "-");    //check lengths of whole and parts    if(keysplit.length != 2        || keysplit[0].length() != 4        || keysplit[1].length() != 4) {        return false;    }    //check if first part is numeric    if(!stringutils.isnumeric(keysplit[0])){        return false;    }    //check if second part contains only    //the four characters 'j', 'a', 'v' and 'a'    if(! stringutils.containsonly(keysplit[1]            ,new char[]{'j', 'a', 'v', 'a'})){        return false;    }    //check if the fourth character   //in the first part is a '0'    if(stringutils.indexof(keysplit[0], '0') != 3){        return false;    }    //if all conditions are fulfilled, key is valid.    return true;}
  在清单1中,我们使用了org.apache.commons.lang.stringutils类中提供的各种方法,根据我们先前定义的所有规则对字符串进行验证。isblank()方法检查字符串是否为空。deletewhitespace()方法确保字符串不包含空格。然后我们使用split()方法对字符串进行分隔,并使用isnumeric()、containsonly()和indexof()方法对密钥的两部分进行验证。
  请注意,尽管在j2se中已经有了indexof()方法,最好使用stringutils中的indexof()。与j2se indexof()方法不同,使用stringutils indexof()时无需担心空指针的问题。触发nullpointerexception被认为是java程序员最常犯的错误。lang可以确保您不会犯同样的错误。即使向indexof()或其他此类方法传递一个null,都不会引发nullpointerexception。indexof()将直接返回-1。
  这样,只需几行简单代码,就可以实现相应的功能,而采用其他方法需要编写很多行代码,而且十分麻烦。如果使用清单2中所示的主方法执行checklicensekey()方法,所得到的结果如清单3所示。

清单2. main()方法
public static void main(string[] args) {    string []key= {"1210-jvaj","1211-jvaj", "210-jvaj", "1210-zvaj"};    for (int i=0; i < key.length; i++){        if(checklicensekey(key[i])){            system.out.println(key[i]+ " >> is valid");        }        else{            system.out.println(key[i]+ " >> is invalid");        }    }}
清单3. 输出
1210-jvaj >> is valid1211-jvaj >> is invalid210-jvaj >> is invalid1210-zvaj >> is invalid
  大部分用于进行字符串操作的方法都隶属于org.apache.commons.lang.stringutils,但也有其他的类可以使用。charutils和charsetutils分别提供使用字符和字符集的实用方法。wordutils是在2.0版中首次出现的类,用于承载专门用于处理字的实用方法。不过,由于字符串和字的处理上有大量的重叠操作,使得此类似乎有点没有存在的必要了。randomstringutils类可以根据各种规则生成随机字符串。
  现在,让我们了解一下lang的另一个有用的方面:处理日期和时间的能力。

时间技术
  在java中处理日期和时间是一件相当棘手的事。如果要使用java.text.simpledateformat、java.util.calendar、java.util.date等类,需要一定时间来适应,还需要对每一个涉及到的类和接口非常了解,才能顺利地处理日期和时间。
  lang组件彻底地简化了日期的处理,并可对其进行格式化。您可以轻松地格式化日期以进行显示、比较日期、舍入或截断日期,甚至能获取特定范围内的所有日期。

清单4. 处理日期和时间
public static void main(string[] args) throws interruptedexception, parseexception {    //date1 created    date date1= new date();    //print the date and time at this instant    system.out.println("the time right now is >>"+date1);    //thread sleep for 1000 ms    thread.currentthread().sleep(dateutils.millis_in_second);    //date2 created.    date date2= new date();    //check if date1 and date2 have the same day    system.out.println("is same day >> "        + dateutils.issameday(date1, date2));    //check if date1 and date2 have the same instance    system.out.println("is same instant >> "        +dateutils.issameinstant(date1, date2));    //round the hour    system.out.println("date after rounding >>"        +dateutils.round(date1, calendar.hour));    //truncate the hour    system.out.println("date after truncation >>"        +dateutils.truncate(date1, calendar.hour));    //three dates in three different formats    string [] dates={"2005.03.24 11:03:26", "2005-03-24 11:03", "2005/03/24"};    //iterate over dates and parse strings to java.util.date objects    for(int i=0; i < dates.length; i++){        date parseddate= dateutils.parsedate(dates[i],        new string []{"yyyy/mm/dd", "yyyy.mm.dd hh:mm:ss", "yyyy-mm-dd hh:mm"});        system.out.println("parsed date is >>"+parseddate);    }    //display date in hh:mm:ss format    system.out.println("now >>"        +dateformatutils.iso_time_no_t_format.format(system.currenttimemillis()));}
  清单4演示了org.apache.commons.lang.dateutils和org.apache.commons.lang.dateformatstringutils类的部分功能。还有其他许多方法可以进行同样的操作,但输入格式不同。故而,如果万一您必须分析和格式化一个日期值,只需要借助提供的方法之一,利用一行代码就可以实现了。
  执行清单4中代码后的输入如清单5所示。

清单5. 输出
the time right now is >>sat apr 09 14:40:41 gmt+05:30 2005is same day >> trueis same instant >> falsedate after rounding >>sat apr 09 15:00:00 gmt+05:30 2005date after truncation >>sat apr 09 14:00:00 gmt+05:30 2005parsed date is >>thu mar 24 11:03:26 gmt+05:30 2005parsed date is >>thu mar 24 11:03:00 gmt+05:30 2005parsed date is >>thu mar 24 00:00:00 gmt+05:30 2005now >>14:40:43
  在清单4中,我们创建了两个日期,这两个日期仅有一秒的差别。然后使用issameinstant()和issameday()方法检查这两个日期是否相同。接下来将日期进行舍入和截断,然后使用在数组中指定的各种格式对特殊日期用例进行解析。
  将您的应用程序集成到第三方应用程序时,经常不能完全确定输入的格式。我曾经做过一个对旧版应用程序的集成,对于每个问题,该应用程序似乎总是有三个答案。所以,如果必须对此类应用程序提供的日期进行解析,您需要提供三个和四个不同的日期格式。清单4中使用parsedate()方法就是为了完成这项任务。这样,即使输入有变化,仍然能更对日期进行解析。还要注意,数组内模式的顺序与输入的顺序并不相同,但该方法仍然找到了适当的模式,并据此进行解析。
  最后,我们按照iso_time_no_t_format格式(hh:mm:ss)对日期进行格式化并打印输入。现在我们将了解使用lang生成常用方法tostring()。

生成tostring()方法
  经常要用到equals()、tostring()和hashcode()方法。不过,谈到实际编写这些方法的实现时,不仅我们大多数人不愿意这样做,而且我们也不能确定如何准确简单地编写这些方法。生成器程序包提供了一些实用类,可以帮助您方便地创建这些方法的实现。大多数情况下,只需要一行代码即可。下面我们将了解lang的tostring功能。

tostring()方法
  您可能没有注意到,在清单4中,即使我们向system.out.println()传递一个java.util.date对象,所获得的输出仍然是正确的日期和时间显示。传递对象引用时,将自动调用tostring()方法,所以可以实现这一点。那么,在我们的示例中实际上调用了java.util.date类的tostring()方法,我们能够得到正确的输出是因为有人重写了java.util.date类中的java.lang.object类的tostring()方法。
  如果没有重写tostring()方法,则获得的输出只是类名称和hashcode的名称。将不会显示类中的任何数据。所以,如果编写了一个新类,且希望能得到正确的打印输出,则需要重写该类中的tostring()方法。

清单6. tostring()方法
public class computer {    string processor;    string color;    int cost;    /** creates a new instance of computer */    public computer(string processor, string color, int cost) {        this.processor=processor;        this.color=color;        this.cost=cost;    }    public static void main(string[] args) {        computer mycomp=new computer("pentium","black",1000);        system.out.println(mycomp);    }        public string tostring(){        return tostringbuilder.reflectiontostring(this);        /*        return tostringbuilder.reflectiontostring(this            , tostringstyle.short_prefix_style);        return tostringbuilder.reflectiontostring(this            , tostringstyle.multi_line_style);        return new tostringbuilder(this)            .append("processor", processor).tostring();         */    }}
  清单6演示了具有三个字段的computer类。其中值得关注的是tostring()方法。调用reflectiontostring()方法可以判断哪些是类中的字段,然后打印其名称和值。main()方法中,我们只创建了该类的一个实例,然后将其打印出来。该类的输出为dev2dev.computer@f6a746[processor=pentium,color=black,cost=1000]。
  因而,如果不希望花太多精力,但又需要您的类有tostring()实现,最简单的方法莫过于将这两行代码复制并粘贴到您的所有类中。如果希望更好地控制生成的结果,请参见注释中提到的选项。通过创建tostringbuilder的新实例,可以对输出应用各种样式,甚至生成全部输出。如果按照列出的顺序执行了全部四个返回语句,则输出如清单7所示。

清单7. 基于tostringbuilder方法的四个可能输出
1) dev2dev.computer@f6a746[processor=pentium,color=black,cost=1000]2) computer[processor=pentium,color=black,cost=1000]3) dev2dev.computer@f6a746[   processor=pentium   color=black   cost=1000   ]4) dev2dev.computer@192d342[processor=pentium]
对象比较与排序
  经常需要对数据对象进行比较,并据此进行排序。那么我们如何对清单6中所给出的computer类的对象进行比较和排序呢?
  您猜猜!让我们使用lang根据计算机的成本对computer对象进行排序。若要比较对象,需要实现java.lang.comparable接口。此接口具有惟一的方法compareto(object)。此方法实现将当前对象和传递给此方法的对象进行比较。如果此对象小于、等于或大于指定的对象,此方法将分别返回负数、零或正数。
  为了对computer对象进行比较,在computer类中实现compareto() 方法,如清单8所示。

清单8. compareto()方法
public int compareto(object obj) {    computer anothercomputer = (computer)obj;    //return new comparetobuilder().reflectioncompare(this, anothercomputer);    return new comparetobuilder().   append(this.cost, anothercomputer.cost).tocomparison();}
  然后,为了实际进行比较,我们编写一个名为computersor的简单类,如清单9中所示。我们只向arraylist添加三个对象,然后进行排序。

清单9. computersort类
public class computersort  {    public static void main(string[] args) {        arraylist computerlist = new arraylist();        computerlist.add(new computer("pentium","black", 1000));        computerlist.add(new computer("pentium","chocolate", 334));        computerlist.add(new computer("pentium","darkgray", 2234));        collections.sort(computerlist);        system.out.println(computerlist);    }}
  执行computersort时,将看到对象根据cost字段的值进行了排序。comparetobuilder与tostringbuilder类似,也有一个基于反射的用法选项。我们已对清单8中的compareto()方法中的位元进行了注释,因为,在此种情况下,反射选项将比较所有字段,最终获得不正确的结果。如果既不希望比较当前类中的字段,也不希望比较其超类中的字段,comparetobuilder也提供了可以用于此用途的方法。执行computersort类的输出如清单10中所示。

清单10. 排序后的computer对象
[dev2dev.computer@cf2c80[processor=pentium,color=chocolate,cost=334], dev2dev.computer@12dacd1[processor=pentium,color=black,cost=1000], dev2dev.computer@1ad086a[processor=pentium,color=darkgray,cost=2234]]

结束语
  本文中,我们了解了jakarta commons lang组件的一些主要功能。总的说来,commons项目是非常有用的项目,但并没有得到充分利用。虽然开源项目使用了许多commons组件,但其在开源世界之外的应用并不十分广泛。现在您已经对lang的功能有所了解,应该考虑立即将其应用到您的应用程序中。另外,还可以在commons项目中寻找各种有用的组件,从而简化xml解析、使应用程序进行http会话、实现系统化验证以及执行很多其他功能。
     本文例子源代码(example.zip)

扫描关注微信公众号