目前Commons Proper中有33个项目,Commons Sandbox中有22个项目,故而,M一cJava目都有其存在的意义?br /> Langlg是Jakarta Commons中较为流行的lg之一。Lang是要呈现在J2SE本n中的一l类? 只有当密钥满x有这些条件时Q应用程序才会查询数据库Q检查该密钥是否合法? l束?/strong> Harshad Oak是Java J2EE门户|站IndicThreads.com的创始h。他~写了Pro Jakarta Commons和Oracle JDeveloper 10g:Empowering J2EE DevelopmentQƈ与h合著了Java 2 Enterprise Edition 1.4 Bible。他q是Rightrix Solutions的创始h?
在本文中Q我们将了解Lang最有用的一些功能。要注意的是Q也可以只用基本JavacL完成Lang的每个功能,但相对于自己学习、理解ƈ~写代码而言Q用Lang要简单得多。即使您能够写出最好的代码Q用经q实验和试的Lang的功能会更快一些,能节省大量的查与试旉。随着Lang一h供了Ҏ的JUnit试用例QLang的用极其广泛,已经历了其创和现实世界的种U考验?br /> Lang的一个重要特征是其简单性。通常来说Q新的Javalg十分复杂Q要了解若干U技术才能用这些组件。要理解lg的用途通常都很难,更别说实际用该lg了。但对于大多数Commonslg而言Q这׃是问题了。Lang一cȝ件用方便,无论对Java初学者还是高UJava用户都非常有用?br /> 如果在采用技术前需要有十的保证,则请在保存您的Java软g的目录中搜烦commons-lang*.jar。结果会让您感到很意外?Tomcat?a target="_blank">Struts?a target="_blank">Hibernate?a target="_blank">Spring?a target="_blank">WebWork {常见Java目都用Lang?br /> 首先让我们看一看用Langq行大多数开发h员几乎每天必进行的操作——字W串操作?
处理字符?/strong>
无论应用E序是基于Swing、J2EE或J2ME的,它都必须使用字符丌Ӏ所以,管在Java中用字W串相当单,但是如果希望按照一定的条g修改和处理字W串Q事情就不那么简单了。您不得不在各种与字W串相关的类中寻扑U不常用的方法,然后惛_法其协同工作,以获得所需的结果。虽然有些LangҎ与J2SE中的某些Ҏ重叠Q但在大多数情况下,一个LangҎ可提供各种cM的多个J2SEҎ的功能,从而帮助您获得所需的输出?
Langlg有许多专门用于字W串操作的类。现在我们将使用一个简单的Java应用E序来演CZ些较为有用的cdҎ?
当应用程序接受用戯入时Q由于用户可能会存在输入错误的情况,或用户可能以各种格式输入数据Q而您希望只采用一U格式进行处理和存储Q则通常会涉及到对字W串q行操作?
例如Q您有一个带输入框的H体Q用户在此输入框内输入许可证密钥。您希望允许输入1111-JAVA格式的密钥。您必须q行以下操作Q?/p>
如果不花大量的时间浏览String、StringTokenizer和其他类的API文档Q您能完成以上的d么?我不能,所以现在我试着用Langlg来管理验证?br />清单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;
}
在清?中,我们使用了org.apache.commons.lang.StringUtilscM提供的各U方法,Ҏ我们先前定义的所有规则对字符串进行验证。isBlank()Ҏ查字W串是否为空。deleteWhitespace()Ҏ保字符串不包含I格。然后我们用split()Ҏ对字W串q行分隔Qƈ使用isNumeric()、containsOnly()和indexOf()Ҏ对密钥的两部分进行验证?br /> h意,管在J2SE中已l有了indexOf()ҎQ最好用StringUtils中的indexOf()。与J2SE indexOf()Ҏ不同Q用StringUtils indexOf()时无需担心I指针的问题。触发NullPointerException被认为是JavaE序员最常犯的错误。Lang可以保您不会犯同样的错误。即使向indexOf()或其他此cL法传递一个nullQ都不会引发NullPointerException。indexOf()直接返?1?br /> q样Q只需几行单代码,可以实现相应的功能Q而采用其他方法需要编写很多行代码Q而且十分ȝ。如果用清?中所C的L法执行checkLicenseKey()ҎQ所得到的结果如清单3所C?br />
清单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 Valid
1211-JVAJ >> Is InValid
210-JVAJ >> Is InValid
1210-ZVAJ >> Is InValid
大部分用于进行字W串操作的方法都隶属于org.apache.commons.lang.StringUtilsQ但也有其他的类可以使用。CharUtils和CharSetUtils分别提供使用字符和字W集的实用方法。WordUtils是在2.0版中首次出现的类Q用于承载专门用于处理字的实用方法。不q,׃字符串和字的处理上有大量的重叠操作,使得此类g有点没有存在的必要了。RandomStringUtilscd以根据各U规则生成随机字W串?
现在Q让我们了解一下Lang的另一个有用的斚wQ处理日期和旉的能力?br />
旉技?/strong>
在Java中处理日期和旉是一件相当棘手的事。如果要使用java.text.SimpleDateFormat、java.util.Calendar、java.util.Date{类Q需要一定时间来适应Q还需要对每一个涉及到的类和接口非怺解,才能利地处理日期和旉?
Langlgd地简化了日期的处理,q可对其q行格式化。您可以L地格式化日期以进行显C、比较日期、舍入或截断日期Q甚臌获取特定范围内的所有日期?
清单4. 处理日期和时?/strong>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.DateFormatStringUtilscȝ部分功能。还有其他许多方法可以进行同L操作Q但输入格式不同。故而,如果万一您必d析和格式化一个日期|只需要借助提供的方法之一Q利用一行代码就可以实现了?
执行清单4中代码后的输入如清单5所C?br />
清单5. 输出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
在清?中,我们创徏了两个日期,q两个日期仅有一U的差别。然后用isSameInstant()和isSameDay()Ҏ查这两个日期是否相同。接下来日期进行舍入和截断Q然后用在数组中指定的各种格式对特D日期用例进行解析?br /> 您的应用程序集成到W三方应用程序时Q经怸能完全确定输入的格式。我曄做过一个对旧版应用E序的集成,对于每个问题Q该应用E序gL有三个答案。所以,如果必须Ҏcd用程序提供的日期q行解析Q您需要提供三个和四个不同的日期格式。清?中用parseDate()Ҏ是Z完成q项d。这P即输入有变化,仍然能更Ҏ期进行解析。还要注意,数组内模式的序与输入的序q不相同Q但该方法仍然找C适当的模式,q据此进行解析?br /> 最后,我们按照ISO_TIME_NO_T_FORMAT格式QHH:mm:ssQ对日期q行格式化ƈ打印输入。现在我们将了解使用Lang生成常用ҎtoString()?br />
生成toString()Ҏ
l常要用到equals()、toString()和hashCode()Ҏ。不q,谈到实际~写q些Ҏ的实现时Q不仅我们大多数Z愿意q样做,而且我们也不能确定如何准简单地~写q些Ҏ。生成器E序包提供了一些实用类Q可以帮助您方便地创些方法的实现。大多数情况下,只需要一行代码即可。下面我们将了解Lang的toString功能?br />
toString()Ҏ
您可能没有注意到Q在清单4中,即我们向System.out.println()传递一个java.util.Date对象Q所获得的输Z然是正确的日期和旉昄。传递对象引用时Q将自动调用toString()ҎQ所以可以实现这一炏V那么,在我们的CZ中实际上调用了java.util.DatecȝtoString()ҎQ我们能够得到正的输出是因为有人重写了java.util.DatecM的java.lang.ObjectcȝtoString()Ҏ?
如果没有重写toString()ҎQ则获得的输出只是类名称和hashcode的名U。将不会昄cM的Q何数据。所以,如果~写了一个新c,且希望能得到正确的打印输出,则需要重写该cM的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演示了具有三个字D늚ComputercR其中值得x的是toString()Ҏ。调用reflectionToString()Ҏ可以判断哪些是类中的字段Q然后打印其名称和倹{main()Ҏ中,我们只创Z该类的一个实例,然后其打印出来。该cȝ输出为dev2dev.Computer@f6a746[processor=Pentium,color=black,cost=1000]?br /> 因而,如果不希望花太多_֊Q但又需要您的类有toString()实现Q最单的Ҏ莫过于将q两行代码复制ƈ_脓到您的所有类中。如果希望更好地控制生成的结果,请参见注释中提到的选项。通过创徏ToStringBuilder的新实例Q可以对输出应用各种样式Q甚至生成全部输出。如果按照列出的序执行了全部四个返回语句,则输出如清单7所C?br />
清单7. ZToStringBuilderҎ的四个可能输?/strong>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]
对象比较与排?/strong>
l常需要对数据对象q行比较Qƈ据此q行排序。那么我们如何对清单6中所l出的Computercȝ对象q行比较和排序呢Q?
您猜猜!让我们用LangҎ计算机的成本对Computer对象q行排序。若要比较对象,需要实现java.lang.Comparable接口。此接口h惟一的方法compareTo(Object)。此Ҏ实现当前对象和传递给此方法的对象q行比较。如果此对象于、等于或大于指定的对象,此方法将分别q回负数、零或正数?
Z对Computer对象q行比较Q在ComputercM实现compareTo() ҎQ如清单8所C?br />
清单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();
}
然后Qؓ了实际进行比较,我们~写一个名为ComputerSor的简单类Q如清单9中所C。我们只向ArrayListd三个对象Q然后进行排序?br />
清单9. ComputerSortc?/strong>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字段的D行了排序。CompareToBuilder与ToStringBuildercMQ也有一个基于反的用法选项。我们已Ҏ?中的compareTo()Ҏ中的位元q行了注释,因ؓQ在此种情况下,反射选项比较所有字D,最l获得不正确的结果。如果既不希望比较当前类中的字段Q也不希望比较其类中的字段QCompareToBuilder也提供了可以用于此用途的Ҏ。执行ComputerSortcȝ输出如清?0中所C?br />
清单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 Langlg的一些主要功能。ȝ说来QCommons目是非常有用的目Q但q没有得到充分利用。虽然开源项目用了许多CommonslgQ但其在开源世界之外的应用q不十分q泛。现在您已经对Lang的功能有所了解Q应该考虑立即其应用到您的应用程序中。另外,q可以在Commons目中寻扑U有用的lgQ从而简化XML解析、应用E序q行HTTP会话、实现系l化验证以及执行很多其他功能?br />
下蝲
下蝲本文中用到的代码Q?a >examples.zip (3KB)
参考资?/strong>