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中的大部分集合類不支持泛型,這讓一些「泛型控」們很不舒服,總想自己再封裝一下,提供一些泛型支持,這就需要讀者在項目開發中自行考慮了。