讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議141:Apache擴展包 >

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