分享
 
 
 

利用Jakarta Commons Lang简化Java

王朝java/jsp·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

摘要

作为企业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:Commons Proper中的项目已可以投入实际使用。

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。结果会让您感到很意外。 Tomcat、Struts、Hibernate、Spring和WebWork 等常见Java项目都使用Lang。

首先让我们看一看使用Lang进行大多数开发人员几乎天天必须进行的操作——字符串操作。

处理字符串

无论应用程序是基于Swing、J2EE或J2ME的,它都必须使用字符串。所以,尽管在Java中使用字符串相当简单,但是假如希望按照一定的条件修改和处理字符串,事情就不那么简单了。您不得不在各种与字符串相关的类中寻找各种不常用的方法,然后想办法使其协同工作,以获得所需的结果。虽然有些Lang方法与J2SE中的某些方法重叠,但在大多数情况下,一个Lang方法就可提供各种类中的多个J2SE方法的功能,从而帮助您获得所需的输出。

Lang组件有许多专门用于字符串操作的类。现在我们将使用一个简单的Java应用程序来演示一些较为有用的类和方法。

当应用程序接受用户输入时,由于用户可能会存在输入错误的情况,或用户可能以各种格式输入数据,而您希望只采用一种格式进行处理和存储,则通常会涉及到对字符串进行操作。

例如,您有一个带输入框的窗体,用户在此输入框内输入许可证密钥。您希望答应输入1111-JAVA格式的密钥。您必须进行以下操作:

检查是否为空字符串。

忽略空格。

密钥区分大小写。

用“-”标记分隔密钥字符串,然后检查第一部分是否全部是数字,第二部分包含的字符是否只来自有效字符集“J”、“A”、“V”、“A”。

两个部分均应有四个字符。

第一部分的第四个数字应该是“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 Valid

1211-JVAJ >> Is InValid

210-JVAJ >> Is InValid

1210-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 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()方法检查这两个日期是否相同。接下来将日期进行舍入和截断,然后使用在数组中指定的各种格式对非凡日期用例进行解析。

将您的应用程序集成到第三方应用程序时,经常不能完全确定输入的格式。我曾经做过一个对旧版应用程序的集成,对于每个问题,该应用程序似乎总是有三个答案。所以,假如必须对此类应用程序提供的日期进行解析,您需要提供三个和四个不同的日期格式。清单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]]

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有