Apache Commons通用扩展包基本上是每个项目都会使用的,只是使用的多少不同而已,一般情况下lang包用作JDK的基础语言扩展,Collections用作集合扩展,DBCP用作数据库连接池等,考虑到commons的名气很响,下面将对它进行相应的介绍,以备在实际开发中使用。
(1)Lang
Apache的Lang功能实在是太实用了,它的很多工具类都是我们在开发过程中经常会用到的,虽然采用JDK的原始类也可以实现,但会花费更多的精力,而且Lang的更新频度很高,用它时不用担心会有太多的Bug。
字符串操作工具类
JDK提供了String类,也提供了一些基本的操作方法,但是要知道String类在项目中是应用最多的类,这也预示着JDK提供的String工具不足以满足开发需求,Lang包弥补了这个缺陷,它提供了诸如StringUtils(基本的String操作类)、StringEscapeUtils(String的转义工具)、RandomStringUtils(随机字符串工具)等非常实用的工具,简单示例如下:
//判断一个字符串是否为空,null或/"/"都返回true
StringUtils.isEmpty(str);
//是否是数字
StringUtils.isNumeric(str);
//最左边两个字符
StringUtils.left(str,2);
//统计子字符串出现的次数
StringUtils.countMatches(str, subString);
//转义XML标示
StringEscapeUtils.escapeXml(str);
//随机生成,长度为10的仅字母的字符串
RandomStringUtils.randomAlphabetic(10);
//随机生成,长度为10的ASCII字符串
RandomStringUtils.randomAscii(10);
//以一个单词为操作对象,首字母大写,输出结果为:Abc Bcd
WordUtils.capitalize(/"abc bcd/");
Object工具类
每个类都有equals、hashCode、toString方法,如果我们自己编写的类需要覆写这些方法,就需要考虑很多的因素了,特别是equals方法,可以参考第3章有关equals的建议,如果我们使用lang包就会简单得多,示例代码如下:
class Person{
private String name;
private int age;
/*getter/setter省略*/
//自定义输出格式
public String toString(){
return new ToStringBuilder(this)
.append(/"姓名/",name)
.append(/"年龄/",age)
.toString();
}
public boolean equals(Object obj){
if(obj==null){
return false;
}
if(obj==this){
return true;
}
if(obj.getClass()!=getClass()){
return false;
}
Person p=(Person)obj;
//只要姓名相同,就认为两个对象相等
return new EqualsBuilder()
.appendSuper(super.equals(obj))
.append(name, p.name)
.isEquals();
}
//自定义hashCode
public int hashCode(){
return HashCodeBuilder.reflectionHashCode(this);
}
}
可变的基本类型
基本类型都有相应的包装类型,但是包装类型不能参与加、减、乘、除运算,要运算还得转化为基本类型,那如果希望使用包装类进行运算该怎么办呢?使用Lang包的示例如下:
//声明一个可变的int类型
MutableInt mi=new MutableInt(10);
//mi加10,结果为20
mi.add(10);
//自加1,结果为21
mi.increment();
其他Utils工具
Lang包在日期处理方面主要提供了DateUtils和DateFormatUtils两个工具类,相比较而言它们没有Joda强大,而且方法也较简单,不再赘述。
Lang包还提供了诸如ArrayUtils、LocaleUtils、NumberUtils等多个工具类,当项目中需要时可以查询一下API,一般情况下都有相应的解决办法。
(2)BeanUtils
它是JavaBean的操作工具包,不仅可以实现属性的拷贝、转换等,还可以建立动态的Bean,甚至建立一些自由度非常高的Bean,我们简单地了解一下它的使用方法。
属性拷贝
在分层开发时经常会遇到PO(Persistence Object)和VO(Value Object)之间的转换问题,不过,有多种方法可以解决之,比如自己写代码PO.setXXX(VO.getXXX()),但是在属性较多的时候容易出错,最好的办法就是使用BeanUtils来操作,代码如下:
//PO对象
User user=new User();
//VO对象
Person person=new Person();
//两个Bean属性拷贝
PropertyUtils.copyProperties(person, user);
//把Map中的键值对拷贝到Bean上
Map<String, String>map=new HashMap<String, String>();
map.put(/"name/",/"张三/");
PropertyUtils.copyProperties(person, map);
动态Bean和自由Bean
我们知道定义一个Bean必然会需要一个类,比如User、Person等,而且还必须在编译期定义完毕,生成.class文件,虽然Bean是一个有固定格式的数据载体,严格要求确实没错,但在某些时候这限制了Bean的灵活性,比如要在运行期生成一个动态Bean,或者在需要生成无固定格式的Bean时,使用普通Bean就无法实现了。我们可以使用BeanUtils包解决该问题,示例代码如下:
//动态Bean,首先定义Bean类
DynaPropertyprops=new DynaProperty{
new DynaProperty(/"name/",String.class),
new DynaProperty(/"age/",int.class)};
BasicDynaClass dynaClass=new BasicDynaClass(/"people/",null, props);
//动态Bean对象
DynaBean people=dynaClass.newInstance();
/*people的get/set操作*/
//自由Bean
DynaBean user=new LazyDynaBean();
//直接定义属性和值
user.set(/"name/",/"张三/");
//定义属性名,限定属性类型为Map
user.set(/"phoneNum/",/"tel/",/"021/");
user.set(/"phoneNum/",/"mobile/",/"138/");
//属性类型为ArrayList
user.set(/"address/",0,/"上海/");
user.set(/"address/",1,/"北京/");
转换器
如果我们期望把一个Bean的所有String类型属性在输出之前都加上一个前缀,该如何做呢?一个一个进行属性过滤?或者使用反射来检查属性类型是否是String,然后加上前缀?这样是可以解决,但不优雅,看BeanUtils如何解决:
//一个简单的Bean对象
User user=new User(/"张三/",18);
//转换工具
ConvertUtilsBean cub=new ConvertUtilsBean();
//注册一个转换器
cub.register(new Converter(){
public Object convert(Class type, Object value){
//为每个String类型的属性加上前缀
return/"prefix-/"+value;
}
},String.class);
//建立一个依赖特定转换工具的Bean工具类
BeanUtilsBean beanUtils=new BeanUtilsBean(cub);
//输出结果为:prefix-张三
beanUtils.getProperty(user,/"name/");
(3)Collections
Collections工具包提供了ListUtils、MapUtils等基本集合操作工具,比较常用而且较简单,这里就不再介绍了。需要重点说明的是Collections包中3个不太常用的集合对象,如下所示。
Bag
Bag是Collections中的一种,它可以容纳重复元素,与List的最大不同点是它提供了重复元素的统计功能,比如一个盒子中有100个球,现在要计算出蓝色球的数量,使用Bag就很容易实现,代码如下:
//一个盒子中装了4个球
Bag box=new HashBag(Arrays.asList(/"red/",/"blue/",/"black/",/"blue/"));
//又增加了3个蓝色球
box.add(/"blue/",3);
//球的数量为7
box.size();
//蓝色球数量为5
box.getCount(/"blue/");
lazy系列
有这样一句话“在我需要的时候,你再出现”,lazy系列的集合就是起这样的作用的,在集合中的元素被访问时它才会生成,这也就涉及一个元素的生成问题了,可通过Factory的实现类来完成,示例代码如下:
//把一个List包装成一个lazy类型
List<String>lazy=LazyList.decorate(new ArrayList(),
new Factory(){
public String create(){
return/"A/";
}
});
//访问了第4个元素,此时0、1、2元素为null
String obj=lazy.get(3);
//追加一个元素
lazy.add(/"第五个元素/");
//元素总数为5个
lazy.size();
双向Map
JDK中的Map要求键必须唯一,而双向Map(Bidirectory Map)则要求键、值都必须唯一,也就是键值是一一对应的,此类Map的好处就是既可以根据键进行操作,也可以反向根据值进行操作,比如删除、查询等,示例代码如下:
//key、value都不允许重复的Map
BidiMap bidiMap=new TreeBidiMap();
bidiMap.put(1,/"壹/");
//根据key获取value
bidiMap.get(1);
//根据value获取key
bidiMap.getKey(/"壹/");
//根据value删除键值对
bidiMap.removeValue(/"壹/");
Apache commons项目还有很多非常好用的工具,如DBCP、net、Math等,但是这些包有个缺点,大部分更新比较缓慢,有些扩展类甚至可以说比较陈旧了,例如Collections中的大部分集合类不支持泛型,这让一些“泛型控”们很不舒服,总想自己再封装一下,提供一些泛型支持,这就需要读者在项目开发中自行考虑了。