摘要
作为企业Java开发人员,我们总是需要实现各种功能,如解析XML、使用HTTP、验证输入以及处理日期等。使用Jakarta Commons项目的目的在于创建负责处理所有此类常用任务的组件,从而节约时间,让您集中精力处理核心业务解决方案。在本文中,我们将对Jakarta Commons 项目作简单介绍,然后演示如何使用Jakarta Commons内的Lang组件来处理和简化日常Java任务,比如字符串操作、使用日期和日历、比较数据对象以及对象排序等。对于本文中的所有例子,我们都将使用最新的Lang版本(即2.1版本)。
编者按:本文中的代码是根据Commons Lang的 RC1版本 编写的。其最终版不久将发行。
Commons和Lang组件简介
Jakarta Commons 项目旨在实现可重用的 Java 组件。此项目包含数十个组件,用以简化 Java 的开发,每个组件负责满足一个特定需求。有大量的组件可用,且不仅限于在特定类型的Java应用程序中使用。
项目分类在两个部件中:
目前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。结果会让您感到很意外。 Tomcat、Struts、Hibernate、Spring和WebWork 等常见Java项目都使用Lang。
首先让我们看一看使用Lang进行大多数开发人员几乎每天必须进行的操作——字符串操作。
处理字符串
无论应用程序是基于Swing、J2EE或J2ME的,它都必须使用字符串。所以,尽管在Java中使用字符串相当简单,但是如果希望按照一定的条件修改和处理字符串,事情就不那么简单了。您不得不在各种与字符串相关的类中寻找各种不常用的方法,然后想办法使其协同工作,以获得所需的结果。虽然有些Lang方法与J2SE中的某些方法重叠,但在大多数情况下,一个Lang方法就可提供各种类中的多个J2SE方法的功能,从而帮助您获得所需的输出。
Lang组件有许多专门用于字符串操作的类。现在我们将使用一个简单的Java应用程序来演示一些较为有用的类和方法。
当应用程序接受用户输入时,由于用户可能会存在输入错误的情况,或用户可能以各种格式输入数据,而您希望只采用一种格式进行处理和存储,则通常会涉及到对字符串进行操作。
例如,您有一个带输入框的窗体,用户在此输入框内输入许可证密钥。您希望允许输入1111-JAVA格式的密钥。您必须进行以下操作:
只有当密钥满足所有这些条件时,应用程序才会查询数据库,检查该密钥是否合法。
如果不花大量的时间浏览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()方法对密钥的两部分进行验证。
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 Valid 1211-JVAJ >> Is InValid 210-JVAJ >> Is InValid 1210-ZVAJ >> Is InValid大部分用于进行字符串操作的方法都隶属于org.apache.commons.lang.StringUtils,但也有其他的类可以使用。CharUtils和CharSetUtils分别提供使用字符和字符集的实用方法。WordUtils是在2.0版中首次出现的类,用于承载专门用于处理字的实用方法。不过,由于字符串和字的处理上有大量的重叠操作,使得此类似乎有点没有存在的必要了。RandomStringUtils类可以根据各种规则生成随机字符串。
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类的部分功能。还有其他许多方法可以进行同样的操作,但输入格式不同。故而,如果万一您必须分析和格式化一个日期值,只需要借助提供的方法之一,利用一行代码就可以实现了。
The time right now is >>Sat Apr 09 14:40:41 GMT+05:30 2005 Is Same Day >> true Is Same Instant >> false Date after rounding >>Sat Apr 09 15:00:00 GMT+05:30 2005 Date after truncation >>Sat Apr 09 14:00:00 GMT+05:30 2005 Parsed Date is >>Thu Mar 24 11:03:26 GMT+05:30 2005 Parsed Date is >>Thu Mar 24 11:03:00 GMT+05:30 2005 Parsed Date is >>Thu Mar 24 00:00:00 GMT+05:30 2005 Now >>14:40:43在清单4中,我们创建了两个日期,这两个日期仅有一秒的差别。然后使用isSameInstant()和isSameDay()方法检查这两个日期是否相同。接下来将日期进行舍入和截断,然后使用在数组中指定的各种格式对特殊日期用例进行解析。
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]。
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]对象比较与排序
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添加三个对象,然后进行排序。
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中所示。
[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会话、实现系统化验证以及执行很多其他功能。
下载
下载本文中用到的代码:examples.zip (3KB)
参考资料
Harshad Oak是Java J2EE门户网站IndicThreads.com的创始人。他编写了Pro Jakarta Commons和Oracle JDeveloper 10g:Empowering J2EE Development,并与人合著了Java 2 Enterprise Edition 1.4 Bible。他还是Rightrix Solutions的创始人。
原文出处
http://dev2dev.bea.com/pub/a/2005/04/commons_lang.html