首页 » 编写高质量代码:改善Java程序的151个建议 » 编写高质量代码:改善Java程序的151个建议全文在线阅读

《编写高质量代码:改善Java程序的151个建议》建议141:Apache扩展包

关灯直达底部

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中的大部分集合类不支持泛型,这让一些“泛型控”们很不舒服,总想自己再封装一下,提供一些泛型支持,这就需要读者在项目开发中自行考虑了。