一篇關於apache commons類庫的詳解

1.1. 開篇

在Java的世界,有很多(成千上萬)開源的框架,有成功的,也有不那麼成功的,有聲名顯赫的,也有默默無聞的。在我看來,成功而默默無聞的那些框架值得我們格外的尊敬和關注,Jakarta Commons就是這樣的一個框架。如果你至少參與了一箇中型規模的Java項目,那麼我想有超過一大半的機會你都接觸和使用到了Jakarta Commons,不管你自己有沒有察覺。就我所知,除了Apache Jakarta其他許多開源框架之外,不少所謂的商業框架其實內部有些模塊是借用Commons的,甚至有一些完全就是對Commons的類進行了簡單的封裝。如果真的沒有接觸過也不要緊,當你看到它時,你自然會被它的簡單而強大所吸引。

要提高Java編程水平,一條可以走的路就是學習優秀的開源框架。這又要分兩個層面:應用層面和源碼層面。從應用來說,開源的框架大都可以給你帶來生產力和/或代碼質量的大幅提升;從源碼來說,Java開源框架,尤其是那些大型的優秀的框架,其源碼對廣大Java愛好者來說都是一筆巨大的財富,你可以從中學到許多課本上學不到的東西:編碼習慣、代碼組織、註釋、文檔、如何用Java解決實際問題、特定問題的算法,等等。而這些對於我們的作爲軟件開發者的實際工作而言,相當有意義。

熟悉Jakarta Commons的朋友可能會覺得現在是不是有點過時,因爲有很多功能在J2SE 5.0中已經包含了。其實這個問題看你怎麼去看,一方面,J2SE 5.0畢竟是剛出現不久的Java版本,實際應用中,很多時候我們需要把代碼兼容等級維持在1.3或者1.4,所以很多5.0的功能我們暫時還不能放開手腳去使用;另一方面,鑑於Jakarta在一定程度上反映了一線Java開發人員的實際需求,而目前5.0已經採納了其中許多特性,我們當然也有理由相信未來的Java版本還會繼續參照Jakarta Commons的內容。有這麼一套開發源碼、免費使用、商業友好的優秀API作爲Java自帶API的補充,何樂而不爲呢?

我打算在近期陸續做一些Jakarta Commons的學習筆記放上來,供大家參考。

有關Jakarta的最新動態和詳細信息,可以參考:

http://jakarta.apache.org/commons/index.html

1.2.簡介

Apache Commons是一個非常有用的工具包,解決各種實際的通用問題,下面是一個簡述表,詳細信息訪問 http://jakarta.apache.org/commons/index.html

BeanUtils
Commons-BeanUtils 提供對 Java 反射和自省API的包裝

Betwixt
Betwixt提供將 JavaBean 映射至 XML 文檔,以及相反映射的服務.

Chain
Chain 提供實現組織複雜的處理流程的“責任鏈模式”.

CLI
CLI 提供針對命令行參數,選項,選項組,強制選項等的簡單API.

Codec
Codec 包含一些通用的編碼解碼算法。包括一些語音編碼器, Hex, Base64, 以及URL encoder.

Collections
Commons-Collections 提供一個類包來擴展和增加標準的 Java Collection框架

Configuration
Commons-Configuration 工具對各種各式的配置和參考文件提供讀取幫助.

Daemon
一種 unix-daemon-like java 代碼的替代機制

DBCP
Commons-DBCP 提供數據庫連接池服務

DbUtils
DbUtils 是一個 JDBC helper 類庫,完成數據庫任務的簡單的資源清除代碼.

Digester
Commons-Digester 是一個 XML-Java對象的映射工具,用於解析 XML配置文件.

Discovery
Commons-Discovery 提供工具來定位資源 (包括類) ,通過使用各種模式來映射服務/引用名稱和資源名稱。.

EL
Commons-EL 提供在JSP2.0規範中定義的EL表達式的解釋器.

FileUpload
FileUpload 使得在你可以在應用和Servlet中容易的加入強大和高性能的文件上傳能力

HttpClient
Commons-HttpClient 提供了可以工作於HTTP協議客戶端的一個框架.

IO
IO 是一個 I/O 工具集

Jelly
Jelly是一個基於 XML 的腳本和處理引擎。 Jelly 借鑑了 JSP 定指標籤,Velocity, Cocoon和Xdoclet中的腳本引擎的許多優點。Jelly 可以用在命令行, Ant 或者 Servlet之中。

Jexl
Jexl是一個表達式語言,通過借鑑來自於Velocity的經驗擴展了JSTL定義的表達式語言。.

JXPath
Commons-JXPath 提供了使用Xpath語法操縱符合Java類命名規範的 JavaBeans的工具。也支持 maps, DOM 和其他對象模型。.

Lang
Commons-Lang 提供了許多許多通用的工具類集,提供了一些java.lang中類的擴展功能

Latka
Commons-Latka 是一個HTTP 功能測試包,用於自動化的QA,驗收和衰減測試.

Launcher
Launcher 組件是一個交叉平臺的Java 應用載入器。Commons-launcher 消除了需要批處理或者Shell腳本來載入Java 類。.原始的 Java 類來自於Jakarta Tomcat 4.0 項目

Logging
Commons-Logging 是一個各種 logging API實現的包裹類.

Math
Math 是一個輕量的,自包含的數學和統計組件,解決了許多非常通用但沒有及時出現在Java標準語言中的實踐問題.

Modeler
Commons-Modeler 提供了建模兼容JMX規範的Mbean的機制.

Net
Net 是一個網絡工具集,基於 NetComponents 代碼,包括 FTP 客戶端等等。

Pool
Commons-Pool 提供了通用對象池接口,一個用於創建模塊化對象池的工具包,以及通常的對象池實現.

Primitives
Commons-Primitives提供了一個更小,更快和更易使用的對Java基本類型的支持。當前主要是針對基本類型的 collection。.

Validator
The commons-validator提供了一個簡單的,可擴展的框架來在一個XML文件中定義校驗器 (校驗方法)和校驗規則。支持校驗規則的和錯誤消息的國際化。

1.3. Commons Lang

跟java.lang這個包的作用類似,Commons Lang這一組API也是提供一些基礎的、通用的操作和處理,如自動生成toString()的結果、自動實現hashCode()和equals()方法、數組操作、枚舉、日期和時間的處理等等。目前這組API的版本是2.1,下載地址如下:

http://apache.justdn.org/jakarta/commons/lang/binaries/commons-lang-2.1.zip

http://apache.justdn.org/jakarta/commons/lang/source/commons-lang-2.1-src.zip

其中後一個是源代碼。

這一組API的所有包名都以org.apache.commons.lang開頭,共有如下8個包:

org.apache.commons.lang

org.apache.commons.lang.builder

org.apache.commons.lang.enum

org.apache.commons.lang.enums

org.apache.commons.lang.exception

org.apache.commons.lang.math

org.apache.commons.lang.mutable

org.apache.commons.lang.time

其中的lang.enum已不建議使用,替代它的是緊隨其後的lang.enums包。 lang包主要是一些可以高度重用的Util類;lang.builder包包含了一組用於產生每個Java類中都常使用到的toString()、hashCode()、equals()、compareTo()等等方法的構造器;lang.enums包顧名思義用於處理枚舉;lang.exception包用於處理Java標準API中的exception,爲1.4之前版本提供Nested Exception功能;lang.math包用於處理數字;lang.mutable用於包裝值型變量;lang.time包提供處理日期和時間的功能。

由於Commons的包和類實在很多,不可能一個一個講了,在接下來的專題文章中我就只分別過一下lang、lang.builder、lang.math和lang.time這幾個包和常見的用法,其他的我們可以在用到時臨時參考一下Javadoc。位置就在安裝路徑的

…\commons-lang-2.1\docs\api\index.html

我們首先來看org.apache.commons.lang包,這個包提供了一些有用的包含static方法的Util類。除了6個Exception類和2個已經deprecated的數字類之外,commons.lang包共包含了17個實用的類:

ArrayUtils – 用於對數組的操作,如添加、查找、刪除、子數組、倒序、元素類型轉換等;

BitField – 用於操作位元,提供了一些方便而安全的方法;

BooleanUtils – 用於操作和轉換 boolean 或者 Boolean 及相應的數組;

CharEncoding – 包含了 Java 環境支持的字符編碼,提供是否支持某種編碼的判斷;

CharRange – 用於設定字符範圍並做相應檢查;

CharSet – 用於設定一組字符作爲範圍並做相應檢查;

CharSetUtils – 用於操作 CharSet

CharUtils – 用於操作 char 值和 Character 對象;

ClassUtils – 用於對 Java 類的操作,不使用反射;

ObjectUtils – 用於操作 Java 對象,提供 null 安全的訪問和其他一些功能;

RandomStringUtils – 用於生成隨機的字符串;

SerializationUtils – 用於處理對象序列化,提供比一般 Java 序列化更高級的處理能力;

StringEscapeUtils – 用於正確處理轉義字符,產生正確的 Java JavaScript HTML XML SQL 代碼;

StringUtils – 處理 String 的核心類,提供了相當多的功能;

SystemUtils – java.lang.System 基礎上提供更方便的訪問,如用戶路徑、 Java 版本、時區、操作系統等判斷;

Validate – 提供驗證的操作,有點類似 assert 斷言;

WordUtils – 用於處理單詞大小寫、換行等。

接下來我準備用兩個例子來分別說明ArrayUtils和StringUtils的常見用法。

1.3.1. org.apache.commons.lang.ArrayUtils

數組是我們經常需要使用到的一種數據結構,但是由於Java本身並沒有提供很好的API支持,使得很多操作實際上做起來相當繁瑣,以至於我們實際編碼中甚至會不惜犧牲性能去使用Collections API,用Collection當然能夠很方便的解決我們的問題,但是我們一定要以性能爲代價嗎?ArrayUtils幫我們解決了處理類似情況的大部分問題。來看一個例子:

package sean.study.jakarta.commons.lang;

import java.util.Map;

import org.apache.commons.lang.ArrayUtils;

public class ArrayUtilsUsage {

    public static void main(String[] args) {

        // data setup

        int [] intArray1 = { 2, 4, 8, 16 };

        int [][] intArray2 = { { 1, 2 }, { 2, 4 }, { 3, 8}, { 4, 16 } };

        Object[][] notAMap = {

                { "A" , new Double(100) },

                { "B" , new Double(80) },

                { "C" , new Double(60) },

                { "D" , new Double(40) },

                { "E" , new Double(20) }

        };

        // printing arrays

        System.out.println( "intArray1: " +ArrayUtils.toString(intArray1));

        System.out.println( "intArray2: " +ArrayUtils.toString(intArray2));

        System.out.println( "notAMap: " +ArrayUtils.toString(notAMap));

        // finding items

        System.out.println( "intArray1 contains '8'? "

                + ArrayUtils.contains(intArray1,8));

        System.out.println( "intArray1 index of '8'? "

                + ArrayUtils.indexOf(intArray1,8));

        System.out.println( "intArray1 last index of '8'? "

                +ArrayUtils.lastIndexOf(intArray1, 8));

        // cloning and resversing

        int[] intArray3 =ArrayUtils.clone(intArray1);

        System.out.println( "intArray3: " +ArrayUtils.toString(intArray3));

        ArrayUtils.reverse(intArray3);

        System.out.println( "intArray3 reversed: "

                +ArrayUtils.toString(intArray3));

        // primitive to Object array

        Integer[] integerArray1 =ArrayUtils.toObject(intArray1);

        System.out.println( "integerArray1: "

                +ArrayUtils.toString(integerArray1));

        // build Map from two dimensional array

        Map map = ArrayUtils.toMap(notAMap);

        Double res = (Double) map.get( "C" );

        System.out.println( "get 'C' from map: " + res);

    }

}

以下是運行結果:

intArray1:{2,4,8,16}

intArray2:{{1,2},{2,4},{3,8},{4,16}}

notAMap:{{A,100.0},{B,80.0},{C,60.0},{D,40.0},{E,20.0}}

intArray1 contains'8'? true

intArray1 index of'8'? 2

intArray1 last indexof '8'? 2

intArray3:{2,4,8,16}

intArray3 reversed:{16,8,4,2}

integerArray1:{2,4,8,16}

get 'C' from map:60.0

這段代碼說明了我們可以如何方便的利用ArrayUtils類幫我們完成數組的打印、查找、克隆、倒序、以及值型/對象數組之間的轉換等操作。如果想了解更多,請參考Javadoc。

1.3.2. org.apache.commons.lang.StringUtils

處理文本對Java應用來說應該算是家常便飯了,在1.4出現之前,Java自身提供的API非常有限,如String、StringTokenizer、StringBuffer,操作也比較單一。無非就是查找substring、分解、合併等等。到1.4的出現可以說Java的文字處理上了一個臺階,因爲它支持regular expression了。這可是個重量級而方便的東東啊,缺點是太複雜,學習起來有一定難度。相較而言,Jakarta Commons提供的StringUtils和WordUtils至今還維持着那種簡潔而強大的美,使用起來也很順手。來看一個例子:

package sean.study.jakarta.commons.lang;

import org.apache.commons.lang.StringUtils;

public class StringUtilsAndWordUtilsUsage {

    public static void main(String[] args) {

        // data setup

        String str1 = "" ;

        String str2 = "" ;

        String str3 = "\t" ;

        String str4 = null ;

        String str5 = "123" ;

        String str6 = "ABCDEFG" ;

        String str7 = "Itfeels good to use JakartaCommons.\r\n" ;

        // check for empty strings

        System.out.println( "==============================" );

        System.out.println( "Is str1 blank? " +StringUtils.isBlank(str1));

        System.out.println( "Is str2 blank? " +StringUtils.isBlank(str2));

        System.out.println( "Is str3 blank? " +StringUtils.isBlank(str3));

        System.out.println( "Is str4 blank? " +StringUtils.isBlank(str4));

        // check for numerics

        System.out.println( "==============================" );

        System.out.println( "Is str5 numeric? " +StringUtils.isNumeric(str5));

        System.out.println( "Is str6 numeric? " +StringUtils.isNumeric(str6));

        // reverse strings / whole words

        System.out.println( "==============================" );

        System.out.println( "str6: " + str6);

        System.out.println( "str6reversed: " + StringUtils.reverse(str6));

        System.out.println( "str7: " + str7);

        String str8 = StringUtils.chomp(str7);

        str8 =StringUtils.reverseDelimited(str8, ' ' );

        System.out.println( "str7 reversed whole words : \r\n" + str8);

        // build header (useful to print logmessages that are easy to locate)

        System.out.println( "==============================" );

        System.out.println( "print header:" );

        String padding = StringUtils.repeat( "=" , 50);

        String msg = StringUtils.center( " Customised Header " , 50, "%" );

        Object[] raw = new Object[]{padding, msg,padding};

        String header = StringUtils.join(raw, "\r\n" );

        System.out.println(header);

    }

}

輸出的結果如下:

==============================

Is str1 blank? true

Is str2 blank? true

Is str3 blank? true

Is str4 blank? true

==============================

Is str5 numeric?true

Is str6 numeric?false

==============================

str6: ABCDEFG

str6 reversed:GFEDCBA

str7: It feels goodto use Jakarta Commons.

str7 reversed wholewords :

Commons. Jakarta use to good feelsIt

==============================

print header:

==================================================

%%%%%%%%%%%%%%%Customised Header %%%%%%%%%%%%%%%%

==================================================

從代碼中我們可以大致瞭解到這個StringUtils類簡單而強大的處理能力,從檢查空串(對null的情況處理很得體),到分割子串,到生成格式化的字符串,使用都很簡潔,也很直截了當。

1.3.3.            org.apache.commons.lang.builder

在前面的專題文章中,我們一起過了一遍org.apache.commons.lang包,接下來我們繼續看org.apache.commons.lang.builder這個包。在這裏面我們可以找到7個類,用於幫助我們實現Java對象的一些基礎的共有方法。這7個類分別是:

CompareToBuilder – 用於輔助實現 Comparable.compareTo(Object) 方法;

EqualsBuilder – 用於輔助實現 Object.equals() 方法;

HashCodeBuilder – 用於輔助實現 Object.hashCode() 方法;

ToStringBuilder – 用於輔助實現 Object.toString() 方法;

ReflectionToStringBuilder – 使用反射機制輔助實現 Object.toString() 方法;

ToStringStyle – 輔助 ToStringBuilder 控制輸出格式;

StandardToStringStyle – 輔助 ToStringBuilder 控制標準格式。

我們知道,在實際應用中,其實經常需要在運行過程中判定對象的知否相等、比較、取hash、和獲取對象基本信息(一般是產生log日誌)。然而實現這些compareTo、equals、hashCode、toString其實並非那麼直截了當,甚至稍有不注意就可能造成難以追蹤的bug,而且這些方法手工維護的話,比較繁瑣,也容易出錯。於是Commons Lang在builder這個包中提供了上述輔助類,爲我們簡化這些方法的實現和維護。

來看一個例子:

package sean.study.jakarta.commons.lang;

import java.util.Date;

import org.apache.commons.lang.builder.CompareToBuilder;

import org.apache.commons.lang.builder.EqualsBuilder;

import org.apache.commons.lang.builder.HashCodeBuilder;

import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

public class BuilderUsage {

    public static void main(String[] args) {

        Staff staff1 = new Staff(123, "John Smith" , new Date());

        Staff staff2 = new Staff(456, "Jane Smith" , new Date());

        System.out.println( "staff1's info: " + staff1);

        System.out.println( "staff2's info: " + staff2);

        System.out.println( "staff1's hash code: " +staff1.hashCode());

        System.out.println( "staff2's hash code: " +staff2.hashCode());

        System.out.println( "staff1 equals staff2? " +staff1.equals(staff2));

    }

}

class Staff implements Comparable {

    private long staffId;

    private String staffName;

    private Date dateJoined;

    public Staff() {

    }

    public Staff( long staffId, String staffName, Date dateJoined){

        this.staffId = staffId;

        this.staffName = staffName;

        this.dateJoined = dateJoined;

    }

    public int compareTo(Object o) {

        int res = -1;

        if (o != null &&Staff.class.isAssignableFrom(o.getClass())) {

            Staff s = (Staff) o;

            res = new CompareToBuilder()

                    .append(dateJoined,s.getDateJoined())

                    .append(staffName,s.getStaffName()).toComparison();

        }

        return res;

    }

    public boolean equals(Object o) {

        boolean res = false;

        if (o != null &&Staff.class.isAssignableFrom(o.getClass())) {

            Staff s = (Staff) o;

            res = new EqualsBuilder()

                    .append(staffId,s.getStaffId())

                    .isEquals();

        }

        return res;

    }

    public int hashCode() {

        return new HashCodeBuilder(11,23).append(staffId).toHashCode();

    }

    public String toString() {

        return new ToStringBuilder( this ,ToStringStyle.MULTI_LINE_STYLE)

                .append( "staffId" ,staffId)

                .append( "staffName" ,staffName)

                .append( "dateJoined" ,dateJoined)

                .toString();

    }

    public Date getDateJoined() {

        return dateJoined;

    }

    public void setDateJoined(Date dateJoined) {

        this .dateJoined = dateJoined;

    }

    public long getStaffId() {

        return staffId;

    }

    public void setStaffId(long staffId) {

        this .staffId = staffId;

    }

    public String getStaffName() {

        return staffName;

    }

    public void setStaffName(String staffName) {

        this .staffName = staffName;

    }

}

以下是運行結果:

staff1's info:sean.study.jakarta.commons.lang.Staff@190d11[

  staffId=123

  staffName=John Smith

  dateJoined=Sat Jul 30 13:18:45 CST 2005

]

staff2's info:sean.study.jakarta.commons.lang.Staff@1fb8ee3[

  staffId=456

  staffName=Jane Smith

  dateJoined=Sat Jul 30 13:18:45 CST 2005

]

staff1's hash code:376

staff2's hash code:709

staff1 equals staff2? false

這些builder使用起來都很簡單,new一個實例,append需要參與的信息,最後加上toComparison、isEquals、toHashCode、toString這樣的結尾即可。相應的,如果你不需要這樣級別的控制,也可以使用利用反射機制的版本自動化實現需要的功能,如:

    public int compareTo(Object o) {

        return CompareToBuilder.reflectionCompare( this , o);

    }

    public boolean equals(Object o) {

        return EqualsBuilder.reflectionEquals( this , o);

    }

    public int hashCode() {

        return HashCodeBuilder.reflectionHashCode( this );

    }

    public String toString() {

        return ReflectionToStringBuilder.toString( this );

    }

尤其當我們在項目中不希望過多的參與到對這些對象方法的維護時,採用Commons提供的利用反射的這些API就成了方便而相對安全的一個方案。

1.3.4.            org.apache.commons.lang.math

在Jakarta Commons中,專門處理數學計算的類分別可以在兩個地方找到:一是Commons Lang的org.apache.commons.lang.math包中,二是在Commons Math這個單獨的子項目中。由於後者主要是處理複數、矩陣等,相對使用比較少,在我的筆記中就只簡單講講Commons Lang中的math包。對後者感興趣的可以看看

http://jakarta.apache.org/commons/math/

org.apache.commons.lang.math包中共有10個類,這些類可以歸納成四組:

1- 處理分數的 Fraction 類;

2- 處理數值的 NumberUtils 類;

3- 處理數值範圍的 Range NumberRange IntRange LongRange FloatRange DoubleRange 類;

4- 處理隨機數的 JVMRandom RandomUtils 類。

下面我舉個例子分別說明上述四組的使用方法:

package sean.study.jakarta.commons.lang;

import org.apache.commons.lang.ArrayUtils;

import org.apache.commons.lang.BooleanUtils;

import org.apache.commons.lang.StringUtils;

import org.apache.commons.lang.math.DoubleRange;

import org.apache.commons.lang.math.Fraction;

import org.apache.commons.lang.math.NumberUtils;

import org.apache.commons.lang.math.RandomUtils;

import org.apache.commons.lang.math.Range;

public class LangMathUsage {

    public static void main(String[] args) {

        demoFraction();

        demoNumberUtils();

        demoNumberRange();

        demoRandomUtils();

    }

    public static void demoFraction() {

        System.out.println(StringUtils.center( " demoFraction " , 30, "=" ));

        Fraction myFraction = Fraction.getFraction(144,90);

        // FractionmyFraction = Fraction.getFraction("1 54/90");

        System.out.println( "144/90 as fraction: " + myFraction);

        System.out.println( "144/90 to proper: " +myFraction.toProperString());

        System.out.println( "144/90 as double: " +myFraction.doubleValue());

        System.out.println( "144/90 reduced: " + myFraction.reduce());

        System.out.println( "144/90 reduced proper: "

                +myFraction.reduce().toProperString());

        System.out.println();

    }

    public static void demoNumberUtils() {

        System.out.println(StringUtils.center( " demoNumberUtils " , 30, "=" ));

        System.out.println( "Is 0x3Fa number? "

                +StringUtils.capitalize(BooleanUtils.toStringYesNo(NumberUtils

                        .isNumber( "0x3F" )))+ "." );

        double [] array = { 1.0, 3.4, 0.8, 7.1, 4.6 };

        double max = NumberUtils.max(array);

        double min = NumberUtils.min(array);

        String arrayStr =ArrayUtils.toString(array);

        System.out.println( "Max of " + arrayStr + " is: " + max);

        System.out.println( "Min of " + arrayStr + " is: " + min);

        System.out.println();

    }

    public static void demoNumberRange() {

        System.out.println(StringUtils.center( " demoNumberRange " , 30, "=" ));

        Range normalScoreRange = newDoubleRange(90, 120);

        double score1 = 102.5;

        double score2 = 79.9;

        System.out.println( "Normal score rangeis: " + normalScoreRange);

        System.out.println( "Is "

                + score1

                + "a normal score? "

                + StringUtils

                        .capitalize(BooleanUtils.toStringYesNo(normalScoreRange

                                .containsDouble(score1)))+ "." );

        System.out.println( "Is "

                + score2

                + "a normal score? "

                + StringUtils

                        .capitalize(BooleanUtils.toStringYesNo(normalScoreRange

                                .containsDouble(score2)))+ "." );

        System.out.println();

    }

    public static void demoRandomUtils() {

        System.out.println(StringUtils.center( " demoRandomUtils " , 30, "=" ));

        for (int i = 0; i < 5; i++) {

            System.out.println(RandomUtils.nextInt(100));

        }

        System.out.println();

    }

}

以下是運行結果:

========demoFraction ========

144/90 as fraction:144/90

144/90 to proper: 154/90

144/90 as double:1.6

144/90 reduced: 8/5

144/90 reducedproper: 1 3/5

======demoNumberUtils =======

Is 0x3F a number? Yes.

Max of{1.0,3.4,0.8,7.1,4.6} is: 7.1

Min of{1.0,3.4,0.8,7.1,4.6} is: 0.8

======demoNumberRange =======

Normal score rangeis: Range[90.0,120.0]

Is 102.5 a normal score? Yes.

Is 79.9 a normal score? No.

======demoRandomUtils =======

75

63

40

21

92

以上就是Commons Lang的math包通常的用法。提一下NumberUtils.inNumber(String)方法,它會正確判斷出給定的字符串是否是合法的數值,而我在前面的筆記中提到的StringUtils.isNumeric(String)只能判斷一個字符串是否是由純數字字符組成。Commons Lang的math包的各個類都還有很多實用的方法,遠不止我在例子中用到的這些,如果你感興趣,參照一下Commons Lang的Javadoc吧。

1.3.5.            org.apache.commons.lang.time

好了,來看我在Common Lang中最後要講的一個包:org.apache.commons.lang.time。這個包裏面包含了如下5個類:

DateFormatUtils – 提供格式化日期和時間的功能及相關常量;

DateUtils – Calendar Date 的基礎上提供更方便的訪問;

DurationFormatUtils – 提供格式化時間跨度的功能及相關常量;

FastDateFormat – java.text.SimpleDateFormat 提供一個的線程安全的替代類;

StopWatch – 是一個方便的計時器。

我們還是在一個例子中來看上述各個類的用法吧:

package sean.study.jakarta.commons.lang;

import java.util.Calendar;

import java.util.Date;

import org.apache.commons.lang.StringUtils;

import org.apache.commons.lang.time.DateFormatUtils;

import org.apache.commons.lang.time.DateUtils;

import org.apache.commons.lang.time.FastDateFormat;

import org.apache.commons.lang.time.StopWatch;

public class DateTimeUsage {

    public static void main(String[] args) {

        demoDateUtils();

        demoStopWatch();

    }

    public static void demoDateUtils() {

        System.out.println(StringUtils.center( " demoDateUtils " , 30, "=" ));

        Date date = new Date();

        String isoDateTime =DateFormatUtils.ISO_DATETIME_FORMAT.format(date);

        String isoTime =DateFormatUtils.ISO_TIME_NO_T_FORMAT.format(date);

        FastDateFormat fdf = FastDateFormat.getInstance( "yyyy-MM" );

        String customDateTime =fdf.format(date);

        System.out.println( "ISO_DATETIME_FORMAT: " + isoDateTime);

        System.out.println( "ISO_TIME_NO_T_FORMAT: " + isoTime);

        System.out.println( "Custom FastDateFormat: " +customDateTime);

        System.out.println( "Default format: " + date);

        System.out.println( "Round HOUR: " + DateUtils.round(date,Calendar.HOUR));

        System.out.println( "Truncate HOUR: " +DateUtils.truncate(date, Calendar.HOUR));

        System.out.println();

    }

    public static void demoStopWatch() {

        System.out.println(StringUtils.center( " demoStopWatch " , 30, "=" ));

        StopWatch sw = new StopWatch();

        sw.start();

        operationA();

        sw.stop();

        System.out.println( "operationA used " + sw.getTime() + " milliseconds." );

        System.out.println();

    }

    public static void operationA() {

        try {

            Thread.sleep(999);

        }

        catch (InterruptedException e) {

            // do nothing

        }

    }

}

以下是運行結果:

=======demoDateUtils ========

ISO_DATETIME_FORMAT:2005-08-01T12:41:51

ISO_TIME_NO_T_FORMAT:12:41:51

CustomFastDateFormat: 2005-08

Default format: MonAug 01 12:41:51 CST 2005

Round HOUR: Mon Aug01 13:00:00 CST 2005

Truncate HOUR: MonAug 01 12:00:00 CST 2005

=======demoStopWatch ========

operationA used 1000milliseconds.

具體的調用細節和完整的API請參閱Commons Lang的Javadoc。

1.4. Commons BeanUtils

Jakarta Commons項目提供了相當豐富的API,我們之前瞭解到的Commons Lang只是衆多API的比較核心的一小部分而已。Commons下面還有相當數量的子項目,用於解決各種各樣不同方向的實際問題,BeanUtils就是其中的一個,用於處理JavaBeans。它利用Java的反射機制,從動態的生成對bean的getter和setter的調用代碼,到模擬創建一個動態的bean,等等。這個包看似簡單,卻是很多開源項目的基石:如在著名的Struts和Spring Framework中,我們都能找到BeanUtils的影子。大家猜猜看,有哪位名人是BeanUtils的作者之一?沒錯,就是Struts的創始人Craig McClanahan。

BeanUtils最核心的好處在於:我們在編碼時,並不需要知道我們處理的JavaBeans具體是什麼類型,有哪些屬性,這些信息是可以動態獲取的,甚至我們都可以不必去關心事實上是否存在這樣一個具體的JavaBean類。我們只需要知道有一個JavaBean的實例,我們需要從中取得某個屬性,設定某個屬性的值,或者僅僅是需要一個屬性表。要做到這些,依靠Sun提供的JavaBean規範似乎找不到一個很直接的方式,除非硬編碼,將getXxxx()和setXxxx()直接寫進我們的程序。但是這樣就大大增加了代碼的複雜度、耦合性和維護成本。還好Commons BeanUtils對這個問題提供了一種優雅的解決方案。

我們有兩種途徑獲取Commons BeanUtils的binary:

1- 從Struts、Spring或者任何依賴BeanUtils的開源產品的發行包中找到相應的jar文件;

2- 從 http://www.apache.org/dist/jakarta/commons/beanutils/binaries/ 下載。

Commons BeanUtils的源碼下載地址:

http://www.apache.org/dist/jakarta/commons/beanutils/source/

Commons BeanUtils一共包括如下5個包:

org.apache.commons.beanutils – 核心包,定義一組 Utils 類和需要用到的接口規範

org.apache.commons.beanutils.converters – 轉換 String 到需要類型的類,實現 Converter 接口

org.apache.commons.beanutils.locale –beanutils locale 敏感版本

org.apache.commons.beanutils.locale.converters– converters locale 敏感版本

org.apache.commons.collections – beanutils 使用到的 Collection

其中需要我們特別關注的是這個org.apache.commons.beanutils包,其他包都是起輔助作用的。接下來我們就仔細看一看這個包都有些什麼東東:

[4個接口]

Converter

該接口定義瞭如下方法:

publicjava.lang.Object convert(java.lang.Class type, java.lang.Object value);

只要實現了這個Converter接口並註冊到ConvertUtils類即可被我們的BeanUtils包所使用,它的主要目的是提供將給定的Object實例轉換爲目標類型的算法。我們可以在beanutils.converters包中找到相當多的已經實現的轉換器。

DynaBean

該接口定義的是一個動態的JavaBean,它的屬性類型、名稱和值都是可以動態改變的。

DynaClass

該接口定義的是針對實現了DynaBean接口的類的java.lang.Class對象,提供如getName()、newInstance()等方法。

MutableDynaClass

該接口是對DynaClass的擴展,使得動態bean的屬性可以動態增加或刪除。

[24個類]

BasicDynaBean

DynaBean接口的最精簡實現

BasicDynaClass

DynaClass接口的最精簡實現

BeanUtils

提供通過反射機制填寫JavaBeans屬性的工具/靜態方法

BeanUtilsBean

BeanUtils類的實例化實現,區別於BeanUtils的靜態方法方式,使得自定義的配置得以保持

ConstructorUtils

同MethodUtils類似,不過專注於構造方法

ContextClassLoaderLocal

針對每個classloader的唯一標識

ConvertingWrapDynaBean

包含了標準JavaBean實例的DynaBean實現,使得我們可以使用DynaBean的API來訪問起屬性,同時提供設定屬性時的類型轉換,繼承自並區別於WrapDynaBean

ConvertUtils

提供工具/靜態方法,用於將String對象及其數組轉換爲指定的類型的對象及其數組。

ConvertUtilsBean

ConvertUtils類的實例化實現,區別於ConvertUtils的靜態方法方式,使得自定義的配置得以保持

DynaProperty

用於描述DynaBean的屬性

JDBCDynaClass

爲DynaClass的JDBC實現提供公用的邏輯

LazyDynaBean

懶載入DynaBean,自動往DynaClass添加屬性並提供懶載入List和懶載入Map的功能

LazyDynaClass

實現MutableDynaClass接口的類

LazyDynaMap

爲Map實例提供一個輕量級的DynaBean包裝

MappedPropertyDescriptor

用於描述映射的屬性

MethodUtils

包含了針對一般意義上的方法而非特定屬性的反射工具/靜態方法

MethodUtils.MethodDescriptor

描述通過反射查找某個方法所使用的鍵值

PropertyUtils

提供利用Java反射API調用具體對象的getter和setter的工具/靜態方法

PropertyUtilsBean

PropertyUtils類的實例化實現,區別於PropertyUtils的靜態方法方式,使得自定義的配置得以保持

ResultSetDynaClass

包裝java.sql.ResultSet中的java.sql.Row實例的DynaBean所對應的DynaClass實現

ResultSetIterator

針對ResultSetDynaClass的java.util.Iterator實現

RowSetDynaClass

DynaClass的一種實現,用於在內存中創建一組表示SQL查詢結果的DynaBeans,區別於ResultSetDynaClass,它不需要保持ResultSet打開

WrapDynaBean

DynaBean的一種實現,包含一個標準的JavaBean實例,以便我們可以使用DynaBean的API去訪問它的屬性,區別於ConvertingWrapDynaBean,它不做專門的類型轉換

WrapDynaClass

DynaClass的一種實現,針對那些包裝標準JavaBean實例的DynaBeans

[3個Exception]

(略)

看到這麼多東西是不是有點頭暈?不要慌,看幾個例子就明白了。只要把握好BeanUtils本身要完成的事,就不難理解這些類存在的道理。我們不妨把BeanUtils的基礎應用分解成:訪問JavaBean的屬性、設定JavaBean的屬性、以及創建和使用DynaBeans。這樣來看BeanUtils,你就會覺得簡單清晰得多。

1.4.1.            BeanUtils代碼範例

假定我們有如下兩個標準的JavaBean:

/**Address.java */

package sean.study.commons.beanutils;

public class Address {

    private String zipCode;

    private String addr;

    private String city;

    private String country;

    public Address() {

    }

    public Address(String zipCode, String addr,String city, String country) {

        this .zipCode = zipCode;

        this .addr = addr;

        this .city = city;

        this .country = country;

    }

    public String getAddr() {

        return addr;

    }

    public void setAddr(String addr) {

        this .addr = addr;

    }

    public String getCity() {

        return city;

    }

    public void setCity(String city) {

        this .city = city;

    }

    public String getCountry() {

        return country;

    }

    public void setCountry(String country) {

        this .country = country;

    }

    public String getZipCode() {

        return zipCode;

    }

    public void setZipCode(String zipCode) {

        this .zipCode = zipCode;

    }

}

/**Customer.java */

package sean.study.commons.beanutils;

public class Customer {

    private long id;

    private String name;

    private Address[] addresses;

    public Customer() {

    }

    public Customer( long id, String name, Address[]addresses) {

        this .id = id;

        this .name = name;

        this .addresses = addresses;

    }

    public Address[] getAddresses() {

        return addresses;

    }

    public void setAddresses(Address[] addresses) {

        this .addresses = addresses;

    }

    public long getId() {

        return id;

    }

    public void setId( long id) {

        this .id = id;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this .name = name;

    }

}

我們來看看通常我們是怎樣利用Commons BeanUtils來完成一些基本的JavaBean和DynaBean操作:

package sean.study.commons.beanutils;

import org.apache.commons.beanutils.BasicDynaBean;

import org.apache.commons.beanutils.BasicDynaClass;

import org.apache.commons.beanutils.DynaBean;

import org.apache.commons.beanutils.DynaProperty;

import org.apache.commons.beanutils.PropertyUtils;

import org.apache.commons.lang.StringUtils;

public class BeanUtilsUsage {

    public static void main(String[] args) throws Exception {

        demoNormalJavaBeans();

        demoDynaBeans();

    }

    public static void demoNormalJavaBeans() throws Exception {

        System.out.println(StringUtils.center( " demoNormalJavaBeans " , 40, "=" ));

        // data setup

        Address addr1 = new Address( "CA1234" , "xxx" , "Los Angeles" , "USA" );

        Address addr2 = new Address( "100000" , "xxx" , "Beijing" , "China" );

        Address[] addrs = new Address[2];

        addrs[0] = addr1;

        addrs[1] = addr2;

        Customer cust = new Customer(123, "John Smith" , addrs);

        // accessing the city of first address

        String cityPattern = "addresses[0].city" ;

        String name = (String)PropertyUtils.getSimpleProperty(cust, "name" );

        String city = (String)PropertyUtils.getProperty(cust, cityPattern);

        Object[] rawOutput1 = new Object[] { "The city of customer " ,name,

                "'sfirst address is " , city, "." };

        System.out.println(StringUtils.join(rawOutput1));

        // setting the zipcode of customer'ssecond address

        String zipPattern = "addresses[1].zipCode" ;

        if (PropertyUtils.isWriteable(cust, zipPattern)){

            System.out.println( "Setting zipcode ..." );

            PropertyUtils.setProperty(cust,zipPattern, "200000" );

        }

        String zip = (String)PropertyUtils.getProperty(cust, zipPattern);

        Object[] rawOutput2 = new Object[] { "The zipcode of customer " ,name,

                "'ssecond address is now " , zip, "." };

        System.out.println(StringUtils.join(rawOutput2));

        System.out.println();

    }

    public static void demoDynaBeans() throws Exception {

        System.out.println(StringUtils.center( " demoDynaBeans " , 40, "=" ));

        // creating a DynaBean

        DynaProperty[] dynaBeanProperties = new DynaProperty[] {

                new DynaProperty( "name" , String.class),

                new DynaProperty( "inPrice" , Double.class), 

                new DynaProperty( "outPrice" , Double.class),

        };

        BasicDynaClass cargoClass = new BasicDynaClass( "Cargo" ,BasicDynaBean.class, dynaBeanProperties);

        DynaBean cargo =cargoClass.newInstance();

        // accessing a DynaBean

        cargo.set( "name" , "Instant Noodles" );

        cargo.set( "inPrice" ,new Double(21.3));

        cargo.set( "outPrice" ,new Double(23.8));

        System.out.println( "name: " + cargo.get( "name" ));

        System.out.println( "inPrice: " + cargo.get( "inPrice" ));

        System.out.println( "outPrice: " + cargo.get( "outPrice" ));

        System.out.println();

    }

}

上述代碼運行結果如下:

=========demoNormalJavaBeans ==========

The city of customerJohn Smith's first address is Los Angeles.

Setting zipcode ...

The zipcode ofcustomer John Smith's second address is now 200000.

============demoDynaBeans =============

name: InstantNoodles

inPrice: 21.3

outPrice: 23.8

以上代碼簡單說明了一下BeanUtils常見的基本用法,還有很多高階或者更具體的應用及原理,這裏無法一一講到,而且有很多筆者也不熟悉、不瞭解,對BeanUtils的講解就到此吧。如果你有興趣,或者還不是很清楚爲什麼像這樣動態的或者說鬆散的訪問JavaBean是有必要的,可以把Struts的源碼拿下來研究一下,看看FormBean以及DynaActionForm這些是如何被動態創建的,一定會有收穫。

1.5. Commons Collections

Commons Collections,又是一個重量級的東西,爲Java標準的Collections API提供了相當好的補充。我不知道其他人,就我自己而言,讓我用java.util.Collection及其子類,加上java.util.Collections類提供的操作方法,處理一些簡單的數據結構問題還可以,稍微複雜一點的就覺得有點頭痛,很多細節的地方需要我插入這樣那樣的小邏輯,或者感覺它太死板,不夠靈活,再或者確實有點晦澀吧。再說了,如果我只是處理一般的數據結構問題,爲什麼不自己用數組或者自定義的鏈表來做,再加上Jakarta Commons的Lang提供的ArrayUtils、StringUtils等,已經基本夠了,性能可以保證,那麼還要這個Collections API幹嘛。當然,說到這裏有些偏激了,Collections當然有它存在的道理,能夠把常用的數據結構歸納起來,以通用的方式去維護和訪問,這應該說是一種進步,但是用起來似乎不夠友好。這個時候我就會想,如果Java比現在做得更好用些,或者有一套第三方的API把我的這些需求抽象出來,實現了,該多好。Commons Collections就是這樣一套API。

在這裏可以找到下載鏈接:(binary和src都有)

http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi

目前Commons Collection發佈的最新版本是3.1。建議下載這個3.1版本,頁面上出現的2.1.1是針對2.1不兼容3.0而發佈的升級維護版。

我們先來瀏覽一下它的包結構。一共是12個:

org.apache.commons.collections – CommonsCollections 自定義的一組公用的接口和工具類

org.apache.commons.collections.bag – 實現 Bag 接口的一組類

org.apache.commons.collections.bidimap – 實現 BidiMap 系列接口的一組類

org.apache.commons.collections.buffer – 實現 Buffer 接口的一組類

org.apache.commons.collections.collection – 實現 java.util.Collection 接口的一組類

org.apache.commons.collections.comparators– 實現 java.util.Comparator 接口的一組類

org.apache.commons.collections.functors –Commons Collections 自定義的一組功能類

org.apache.commons.collections.iterators – 實現 java.util.Iterator 接口的一組類

org.apache.commons.collections.keyvalue – 實現集合和鍵 / 值映射相關的一組類

org.apache.commons.collections.list – 實現 java.util.List 接口的一組類

org.apache.commons.collections.map – 實現 Map 系列接口的一組類

org.apache.commons.collections.set – 實現 Set 系列接口的一組類

用過Java Collections API的朋友大概或多或少會同意我如下的劃分:在Java的Collections API中,不狹義的區分語法上的接口和類,把它們都看作是類的話,大致我們可以發現三種主要的類別:

1- 容器類:如Collection、List、Map等,用於存放對象和進行簡單操作的;

2- 操作類:如Collections、Arrays等,用於對容器類的實例進行相對複雜操作如排序等;

3- 輔助類:如Iterator、Comparator等,用於輔助操作類以及外部調用代碼實現對容器類的操作,所謂輔助,概括而通俗的來講,就是這些類提供一種算法,你給它一個對象或者一組對象,或者僅僅是按一定的規則調用它,它給你一個運算後的答案,幫助你正確處理容器對象。比如Iterator會告訴你容器中下一個對象有沒有、是什麼,而Comparator將對象大小/先後次序的算法邏輯獨立出來。

同樣,Jakarta Commons Collections我們細細看來,也能夠找出類似的劃分:

1- 作爲容器類的補充,我們可以找到Bag、Buffer、BidiMap、OrderedMap等等;

2- 作爲操作類的補充,我們可以找到CollectionUtils、IteratorUtils、ListUtils、SetUtils等等;

3- 作爲輔助類的補充,我們可以找到MapIterator、Closure、Predicate、Transformer等等;

對於這樣的一個大包,當然不可能一個類一個類的講了,找一些常用的和有用的當做接下來討論的話題吧。大概列個清單:

Bag

HashBag

BagUtils

Buffer

BlockingBuffer

BoundedFifoBuffer

PriorityBuffer

BufferUtils

MultiMap

BidiMap

CaseInsensitiveMap

LazyMap

MapUtils

TypedCollection

CollectionUtils

ReverseComparator

ComparatorChain

NullComparator

FixedOrderComparator

ComparatorUtils

Predicate

AndPredicate

OrPredicate

AllPredicate

OnePredicate

NonePredicate

PredicateUtils

Transformer

ChainedTransformer

SwitchTransformer

TransformerUtils

Closure

ChainedClosure

IfClosure

WhileClosure

ClosureUtils

LoopingIterator

ArrayListIterator

FilterIterator

UniqueFilterIterator

IteratorUtils

總共9組,在接下來的筆記中我們一起慢慢的看。

1.5.1.            Bag組

首先來看Bag組。

Bag

HashBag

BagUtils

Bag是在org.apache.commons.collections包中定義的接口,它extends java.util.Collection,而它的實現類都被放在下面的bag包中。之所以有這樣一組類型,是因爲我們有時候需要在Collection中存放多個相同對象的拷貝,並且需要很方便的取得該對象拷貝的個數。需要注意的一點是它雖然extends Collection,但是如果真把它完全當作java.util.Collection來用會遇到語義上的問題,詳細信息參考Javadoc。

HashBag是Bag接口的一個標準實現。而BagUtils提供一組static的方法讓調用者獲取經過不同裝飾後的Bag實例。

還是舉例子來看:

/**Book.java */

package sean.study.commons.collections;

import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

public class Book {

    private String name;

    private String isbn;

    private double retailPrice;

    public Book() {

    }

    public Book(String name, String isbn, double retailPrice) {

        this .name = name;

        this .isbn = isbn;

        this .retailPrice = retailPrice;

    }

    public String toString() {

        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

        .append( "name" ,name)

        .append( "ISBN" ,isbn)

        .append( "retailPrice" ,retailPrice)

        .toString();

    }

    public String getIsbn() {

        return isbn;

    }

    public void setIsbn(String isbn) {

        this .isbn = isbn;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this .name = name;

    }

    public double getRetailPrice() {

        return retailPrice;

    }

    public void setRetailPrice( double retailPrice) {

        this .retailPrice = retailPrice;

    }

}

/**BagUsage.java */

package sean.study.commons.collections;

import org.apache.commons.collections.Bag;

import org.apache.commons.collections.BagUtils;

import org.apache.commons.collections.bag.HashBag;

import org.apache.commons.lang.StringUtils;

public class BagUsage {

    public static void main(String[] args) {

        demoBagUsage();

    }

    public static void demoBagUsage() {

        System.out.println(StringUtils.center( " demoBagUsage " , 40, "=" ));

        // data setup

        Book book1 = new Book( "RefactoringWorkbook" , "7-5083-2208-8" ,29.8);

        Book book2 = new Book( "J2EEDesign Patterns" , "7-5083-3099-4" ,45);

        Book book3 = new Book( "AgileSoftware Development" , "7-5083-1503-0" ,59);

        // create a bag

        Bag myBag = BagUtils.typedBag( new HashBag(), Book. class );

        myBag.add(book1, 360);

        myBag.add(book2, 500);

        myBag.add(book3, 170);

        // calculations for a bag

        double price1 = book1.getRetailPrice();

        double price2 = book2.getRetailPrice();

        double price3 = book3.getRetailPrice();

        int book1Count = myBag.getCount(book1);

        int book2Count = myBag.getCount(book2);

        int book3Count = myBag.getCount(book3);

        double totalValue = (price1 * book1Count) +(price2 * book2Count)

                + (price3 * book3Count);

        // dispaly results

        System.out.println( "There are " + book1Count + " copies of "

                + book1.getName() + "." );

        System.out.println( "There are " + book2Count + " copies of "

                + book2.getName() + "." );

        System.out.println( "There are " + book3Count + " copies of "

                + book3.getName() + "." );

        System.out.println( "The total value of these books is: " +totalValue);

        System.out.println();

    }

}

以下是運行結果:

=============demoBagUsage =============

There are 360 copiesof Refactoring Workbook.

There are 500 copiesof J2EE Design Patterns.

There are 170 copiesof Agile Software Development.

The total value ofthese books is: 43258.0

需要說明的是,以上的代碼僅僅爲了演示如何使用Bag,實際應用不建議像這樣硬編碼。

1.5.2.            Buffer組

來看Buffer組。

Buffer

BlockingBuffer

BoundedFifoBuffer

PriorityBuffer

UnboundedFifoBuffer

BufferUtils

Buffer是定義在org.apache.commons.collections包下面的接口,用於表示按一定順序除去成員對象的collection如隊列等。具體的實現類在org.apache.commons.collections.buffer包下可以找到。

BufferUtils提供很多靜態/工具方法裝飾現有的Buffer實例,如將其裝飾成BlockingBuffer、執行類型檢查的TypedBuffer、或者不可改變的UnmodifiableBuffer等等。

最簡單直接的Buffer實現類是UnboundedFifoBuffer,提供先進先出的大小可變的隊列。而BoundedFifoBuffer則是對其大小進行了限制,是固定大小的先進先出隊列。BlockingBuffer要在多線程的環境中才能體現出它的價值,尤其是當我們需要實現某種流水線時這個BlockingBuffer很有用:每個流水線上的組件從上游的BlockingBuffer獲取數據,處理後放到下一個BlockingBuffer中依次傳遞。BlockingBuffer的核心特色通俗點說就是如果你向它要東西,而它暫時還沒有的話,你可以一直等待直至拿到爲止。PriorityBuffer則提供比一般的先進先出Buffer更強的控制力:我們可以自定義Comparator給它,告訴它怎麼判定它的成員的先後順序,優先級最高的最先走。

爲了方便和清晰的需要,我在這裏只舉一個BoundedFifoBuffer,包裝成TypedBuffer,看看在具體的代碼中通常如何使用Buffer:(還是沿用上次的Book類)

package sean.study.commons.collections;

import java.util.Iterator;

import org.apache.commons.collections.Buffer;

import org.apache.commons.collections.BufferUtils;

import org.apache.commons.collections.buffer.BoundedFifoBuffer;

import org.apache.commons.lang.StringUtils;

public class BufferUsage {

    public static void main(String[] args) {

        demoBufferUsage();

    }

    public static void demoBufferUsage() {

        System.out.println(StringUtils.center( " demoBagUsage " , 40, "=" ));

        // data setup

        Book book1 = new Book( "RefactoringWorkbook" , "7-5083-2208-8" ,29.8);

        Book book2 = new Book( "J2EEDesign Patterns" , "7-5083-3099-4" ,45);

        Book book3 = new Book( "AgileSoftware Development" , "7-5083-1503-0" ,59);

        Book book4 = new Book( "ProfessionalJSP" , "7-5053-8005-2" ,100);

        // create a Buffer

        Buffer buffer =

            BufferUtils.typedBuffer( new BoundedFifoBuffer(3), Book. class );

        buffer.add(book1);

        buffer.add(book2);

        buffer.add(book3);

        Book removed = (Book) buffer.remove();

        System.out.println( "Removed:" );

        System.out.println(removed);

        buffer.add(book4);

        // get items in buffer

        for ( int i = 0; i < 3; i++) {

            System.out.println(buffer.get());

            buffer.remove();

        }

        System.out.println(StringUtils.repeat( "=" , 40));

    }

}

以下是運行結果:

=============demoBagUsage =============

Removed:

sean.study.commons.collections.Book@e09713[

  name=Refactoring Workbook

  ISBN=7-5083-2208-8

  retailPrice=29.8

]

Remaining:

sean.study.commons.collections.Book@e09713[

  name=J2EE Design Patterns

  ISBN=7-5083-3099-4

  retailPrice=45.0

]

sean.study.commons.collections.Book@47b480[

  name=Agile Software Development

  ISBN=7-5083-1503-0

  retailPrice=59.0

]

sean.study.commons.collections.Book@19b49e6[

  name=Professional JSP

  ISBN=7-5053-8005-2

  retailPrice=100.0

]

========================================

我們可以看到,Buffer的add和remove方法分別添加新成員和刪除最先加入的成員。由於我們的Buffer定義爲只能裝3個Book類的實例,所以不論我們試圖加入其他類型的對象,或者加入超過3個,操作都將失敗。如果我們在遍歷時使用get()而不調用remove(),那麼我們將得到3個相同的拷貝,而這正是我們期望的FIFO隊列的行爲。假如你需要遍歷並保留數據,可以使用標準的Iterator機制。

1.5.3.            Map組

接下來看Map組。

BidiMap

MultiMap

LazyMap

MapUtils

Commons Collections在java.util.Map的基礎上擴展了很多接口和類,比較有代表性的是BidiMap、MultiMap和LazyMap。跟Bag和Buffer類似,Commons Collections也提供了一個MapUtils。

所謂BidiMap,直譯就是雙向Map,可以通過key找到value,也可以通過value找到key,這在我們日常的代碼-名稱匹配的時候很方便:因爲我們除了需要通過代碼找到名稱之外,往往也需要處理用戶輸入的名稱,然後獲取其代碼。需要注意的是BidiMap當中不光key不能重複,value也不可以。

所謂MultiMap,就是說一個key不在是簡單的指向一個對象,而是一組對象,add()和remove()的時候跟普通的Map無異,只是在get()時返回一個Collection,利用MultiMap,我們就可以很方便的往一個key上放數量不定的對象,也就實現了一對多。

所謂LazyMap,意思就是這個Map中的鍵/值對一開始並不存在,當被調用到時才創建,這樣的解釋初聽上去是不是有點不可思議?這樣的LazyMap有用嗎?我們這樣來理解:我們需要一個Map,但是由於創建成員的方法很“重”(比如數據庫訪問),或者我們只有在調用get()時才知道如何創建,或者Map中出現的可能性很多很多,我們無法在get()之前添加所有可能出現的鍵/值對,或者任何其它解釋得通的原因,我們覺得沒有必要去初始化一個Map而又希望它可以在必要時自動處理數據生成的話,LazyMap就變得很有用了。

我們還是通過一個具體的例子來說明:

package sean.study.commons.collections;

import java.util.Date;

import java.util.HashMap;

import java.util.Map;

import org.apache.commons.collections.BidiMap;

import org.apache.commons.collections.Factory;

import org.apache.commons.collections.MultiHashMap;

import org.apache.commons.collections.MultiMap;

import org.apache.commons.collections.bidimap.DualHashBidiMap;

import org.apache.commons.collections.map.LazyMap;

import org.apache.commons.lang.StringUtils;

public class MapUsage {

    public static void main(String[] args) {

        demoBidiMap();

        demoMultiMap();

        demoLazyMap();

    }

    public static void demoBidiMap() {

        System.out.println(StringUtils.center( " demoBidiMap " , 40, "=" ));

        BidiMap bidiMap = new DualHashBidiMap();

        bidiMap.put( "BJ" , "Beijing" );

        bidiMap.put( "SH" , "Shanghai" );

        bidiMap.put( "GZ" , "Guangzhou" );

        bidiMap.put( "CD" , "Chengdu" );

        System.out.println( "Key-Value: BJ = " + bidiMap.get( "BJ" ));

        System.out.println( "Value-Key: Chengdu =" + bidiMap.getKey( "Chengdu" ));

        System.out.println(StringUtils.repeat( "=" , 40));

    }

    public static void demoMultiMap() {

        System.out.println(StringUtils.center( " demoMultiMap " , 40, "=" ));

        MultiMap multiMap = new MultiHashMap();

        multiMap.put( "Sean" , "C/C++" );

        multiMap.put( "Sean" , "OO" );

        multiMap.put( "Sean" , "Java" );

        multiMap.put( "Sean" , ".NET" );

        multiMap.remove( "Sean" , "C/C++" );

        System.out.println( "Sean's skill set: " + multiMap.get( "Sean" ));

        System.out.println(StringUtils.repeat( "=" , 40));

    }

    public static void demoLazyMap() {

        System.out.println(StringUtils.center( " demoLazyMap " , 40, "=" ));

        // borrowed from Commons Collection'sJavadoc

        Factory factory = new Factory() {

            public Object create() {

                return new Date();

            }

        };

        Map lazy = LazyMap.decorate( new HashMap(), factory);

        System.out.println(lazy.get( "NOW" ));

        System.out.println(StringUtils.repeat( "=" , 40));

    }

}

以下是運行結果:

=============demoBidiMap ==============

Key-Value: BJ = Beijing

Value-Key: Chengdu = CD

========================================

=============demoMultiMap =============

Sean's skill set:[OO, Java, .NET]

========================================

=============demoLazyMap ==============

Wed Aug 03 12:44:56CST 2005

========================================

簡單說一下這個Factory,它是定義在org.apache.commons.collections包下面的一個接口,用於自定義對象的創建過程。這個有點像是後面我們要講的Transformer的簡化版本,但是也更直接也很好用,至少Commons Collections通過它向開發人員開放了一個可以方便控制對象創建細節的接口。

1.5.4.            Collection組

接下來看看Collection組。

TypedCollection

CollectionUtils

首先就是這個TypedCollection,它實際上的作用就是提供一個decorate方法,我們傳進去一個Collection和需要的類型甄別信息java.lang.Class,它給我們創建一個全新的強類型的Collection。我們其實在bag、buffer、list、map、set這些子包中都可以找到分別對應Bag、Buffer、List、Map、Set接口的TypedXxxx版本。

方法簽名:

public static Collection decorate(Collection coll, Classtype)

當它執行時,它會判斷coll是否爲null,同時如果coll包含數據,它會對數據進行驗證,看是否滿足指定的type條件。最後它返回一個強類型的Collection,當我們對這個強類型的Collection進行add操作時,它會幫我們確保添加的是正確的類型。

而這個CollectionUtils可能大家都已經想到了,就是提供一組針對Collection操作的工具/靜態方法。比較有意思的是對Collection的轉型、合併、減等操作。

由於這兩個類的功能和作用都比較清晰,我就不舉例說明了,需要進一步瞭解的請看Javadoc。

1.5.5.            Comparator組

接下來我們會講到輔助類,首先看Comparator組。

ReverseComparator

ComparatorChain

NullComparator

FixedOrderComparator

ComparatorUtils

其實Comparator這個概念並不是Commons Collections引入的,在標準的Java Collections API中,已經明確定了一個java.util.Comparator接口,只是有很多人並不瞭解,Commons Collections也只是擴展了這個接口而已。這個java.util.Comparator定義如下核心方法:

public int compare(Object arg0, Object arg1)

傳給它兩個對象,它要告訴我們這兩個對象哪一個在特定的語義下更“大”,或者兩者相等。如果arg0 > arg1,返回大於0的整數;如果arg0 = arg1,返回0;如果arg0 < arg2,返回小於0的整數。

我們看看Commons Collections給我們提供了哪些Comparator的實現類(都在org.apache.commons.collections.comparators包下面):

BooleanComparator – 用於排序一組 Boolean 對象,指明先 true 還是先 false

ComparableComparator – 用於排序實現了 java.lang.Comparable 接口的對象(我們常用的 Java 類如 String Integer Date Double File Character 等等都實現了 Comparable 接口);

ComparatorChain – 定義一組 Comparator 鏈,鏈中的 Comparator 對象會被依次執行;

FixedOrderComparator – 用於定義一個特殊的順序,對一組對象按照這樣的自定義順序進行排序;

NullComparator – null 值也可參與比較,可以設定爲先 null 或者後 null

ReverseComparator – 將原有的 Comparator 效果反轉;

TransformingComparator – 將一個 Comparator 裝飾爲具有 Transformer 效果的 Comparator

// 有關 Transformer 的內容會在以後的筆記中講到。

以上除了ComparatorChain之外,似乎都是實現一些很基本的比較方法,但是當我們用ComparatorChain將一組Comparator串起來之後,就可以實現非常靈活的比較操作。那麼這些Comparator在實際代碼中如何使用呢?看例子:

/**Issue.java */

package sean.study.commons.collections;

import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

public class Issue {

    private long id;

    private String severity;

    private String owner;

    public Issue() {

    }

    public Issue( long id, String severity, String owner) {

        this .id = id;

        this .severity = severity;

        this .owner = owner;

    }

    public String toString() {

        return new ToStringBuilder( this ,ToStringStyle.SHORT_PREFIX_STYLE)

                .append( "id" ,id)

                .append( "severity" ,severity)

                .append( "owner" ,owner)

                .toString();

    }

    public long getId() {

        return id;

    }

    public void setId( long id) {

        this .id = id;

    }

    public String getOwner() {

        return owner;

    }

    public void setOwner(String owner) {

        this .owner = owner;

    }

    public String getSeverity() {

        return severity;

    }

    public void setSeverity(String severity) {

        this .severity = severity;

    }

}

/**ComparatorUsage.java */

package sean.study.commons.collections;

import java.util.Arrays;

import java.util.Comparator;

import org.apache.commons.beanutils.BeanComparator;

import org.apache.commons.collections.comparators.ComparatorChain;

import org.apache.commons.collections.comparators.FixedOrderComparator;

import org.apache.commons.lang.StringUtils;

public class ComparatorUsage {

    public static void main(String[] args) {

        demoComparator();

    }

    public static void demoComparator() {

        System.out.println(StringUtils.center( " demoComparator " , 40, "=" ));

        // data setup

        Issue[] issues = new Issue[] {

                new Issue(15102, "Major" , "John" ),

                new Issue(15103, "Minor" , "Agnes" ),

                new Issue(15104, "Critical" , "Bill" ),

                new Issue(15105, "Major" , "John" ),

                new Issue(15106, "Major" , "John" ),

                new Issue(15107, "Critical" , "John" ),

                new Issue(15108, "Major" , "Agnes" ),

                new Issue(15109, "Minor" , "Julie" ),

                new Issue(15110, "Major" , "Mary" ),

                new Issue(15111, "Enhancement" , "Bill" ),

                new Issue(15112, "Minor" , "Julie" ),

                new Issue(15113, "Major" , "Julie" )

        };

        // comparators setup

        String[] severityOrder = { "Critical" , "Major" , "Minor" , "Enhancement" };

        Comparator severityComparator = new FixedOrderComparator(severityOrder);

        ComparatorChain compChain = new ComparatorChain();

        compChain.addComparator(newBeanComparator( "owner" ));

        compChain.addComparator(newBeanComparator( "severity" ,severityComparator));

        compChain.addComparator(newBeanComparator( "id" ));

        // sort and display

        Arrays.sort(issues, compChain);

        for ( int i = 0; i < issues.length; i++) {

            System.out.println(issues[i]);

        }

        System.out.println(StringUtils.repeat( "=" , 40));

    }

}

輸出結果爲:

============demoComparator ============

Issue[id=15108,severity=Major,owner=Agnes]

Issue[id=15103,severity=Minor,owner=Agnes]

Issue[id=15104,severity=Critical,owner=Bill]

Issue[id=15111,severity=Enhancement,owner=Bill]

Issue[id=15107,severity=Critical,owner=John]

Issue[id=15102,severity=Major,owner=John]

Issue[id=15105,severity=Major,owner=John]

Issue[id=15106,severity=Major,owner=John]

Issue[id=15113,severity=Major,owner=Julie]

Issue[id=15109,severity=Minor,owner=Julie]

Issue[id=15112,severity=Minor,owner=Julie]

Issue[id=15110,severity=Major,owner=Mary]

========================================

我們可以看到,ComparatorChain中的Comparator被依次執行,先按name,再按我們自定義的severity次序,再按id,最終我們得到了重新排列的數組。

1.5.6.            Predicate組

接下來看Predicate組

Predicate

AndPredicate

OrPredicate

AllPredicate

OnePredicate

NonePredicate

PredicateUtils

Predicate是Commons Collections中定義的一個接口,可以在org.apache.commons.collections包中找到。其中定義的方法簽名如下:

public boolean evaluate(Object object)

它以一個Object對象爲參數,處理後返回一個boolean值,檢驗某個對象是否滿足某個條件。其實這個Predicate以及上一篇筆記提到的Comparator還有我們即將看到的Transformer和Closure等都有些類似C/C++中的函數指針,它們都只是提供簡單而明確定義的函數功能而已。

跟其他組類似,Commons Collections也提供了一組定義好的Predicate類供我們使用,這些類都放在org.apache.commons.collections.functors包中。當然,我們也可以自定義Predicate,只要實現這個Predicate接口即可。在Commons Collections中我們也可以很方便使用的一組預定義複合Predicate,我們提供2個或不定數量個Predicate,然後交給它,它可以幫我們處理額外的邏輯,如AndPredicate處理兩個Predicate,只有當兩者都返回true它才返回true;AnyPredicate處理多個Predicate,當其中一個滿足就返回true,等等。

看看具體的代碼中如何使用這些Predicate吧:

package sean.study.commons.collections;

import org.apache.commons.collections.Predicate;

import org.apache.commons.collections.PredicateUtils;

import org.apache.commons.collections.functors.InstanceofPredicate;

import org.apache.commons.collections.functors.NotNullPredicate;

import org.apache.commons.lang.BooleanUtils;

import org.apache.commons.lang.StringUtils;

public class PredicateUsage {

    public static void main(String[] args) {

        demoPredicates();

    }

    public static void demoPredicates() {

        System.out.println(StringUtils.center( " demoPredicates " , 40, "=" ));

        Predicate p1 = new InstanceofPredicate(String.class);

        Predicate p2 =NotNullPredicate.getInstance();

        Predicate p3 = new Predicate() {

            public boolean evaluate(Object obj) {

                String str = (String) obj;

                return StringUtils.isAlphanumeric(str)

                    && str.length()>= 6

                    && str.length()<= 10;

            }

        };

        Predicate p4 =PredicateUtils.allPredicate( new Predicate[]{p1, p2, p3});                Stringinput = "ABCD1234" ;

        Object[] raw = new Object[] {

            "Is'" ,

            input,

            "' avalid input? " ,

            BooleanUtils.toStringYesNo(p4.evaluate(input)),

            "."

        };

        System.out.println(StringUtils.join(raw));

        System.out.println(StringUtils.repeat( "=" , 40));

    }

}

輸出結果如下:

============demoPredicates ============

Is 'ABCD1234' avalid input? yes.

========================================

這裏面我首先定義了3個簡單的Predicate,p1判斷對象是否爲String的實例,p2判斷是否對象爲null,p3是自定義的,判斷是否爲數字字母的組合,並且長度在6~10字符。然後我用AllPredicate將它們組合到一起,成爲p4,當p1、p2、p3都滿足時,p4的evaluate方法才返回true。利用Predicate我們可以把判斷true或false的邏輯從特定的業務代碼分離出來,以後我們重用也好,重新組裝也好,都是很方便的。

1.5.7.            Transformer組

接下來看Transformer組。

Transformer

ChainedTransformer

SwitchTransformer

TransformerUtils

我們有時候需要將某個對象轉換成另一個對象供另一組方法調用,而這兩類對象的類型有可能並不是出於同一個繼承體系的,或者說出了很基本的Object之外沒有共同的父類,或者我們根本不關心他們是不是有其他繼承關係,甚至就是同一個類的實例只是對我們而言無所謂,我們爲了它能夠被後續的調用者有意義的識別和處理,在這樣的情形,我們就可以利用Transformer。除了基本的轉型Transformer之外,Commons Collections還提供了Transformer鏈和帶條件的Transformer,使得我們很方便的組裝出有意義的轉型邏輯。

假定我們在處理員工聘用時,需要將原來的Applicant對象轉換爲Employee對象,而Applicant類和Employee類無論繼承關係、字段內容、具體業務職能等等都不是同一派系的,只是某些字段是相關的,且要求必要的轉換,那麼這個時候我們使用Transformer就可以比較方便的實現這項功能,並且由於它的實現是靈活的、模塊化的,使得今後的維護也變得清晰和易於處理。代碼如下:

/**Applicant.java */

package sean.study.commons.collections;

import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

public class Applicant {

    private String name;

    private int age;

    private String applyFor;

    public Applicant() {

    }

    public Applicant(String name, int age, String applyFor) {

        this .name = name;

        this .age = age;

        this .applyFor = applyFor;

    }

    public String toString() {

        return new ToStringBuilder( this ,ToStringStyle.SHORT_PREFIX_STYLE)

                .append( "name" ,name)

                .append( "age" ,age)

                .append( "applyFor" ,applyFor)

                .toString();

    }

    public int getAge() {

        return age;

    }

    public void setAge( int age) {

        this .age = age;

    }

    public String getApplyFor() {

        return applyFor;

    }

    public void setApplyFor(String applyFor) {

        this .applyFor = applyFor;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this .name = name;

    }

}

/**Employee.java */

package sean.study.commons.collections;

import java.util.Date;

import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

import org.apache.commons.lang.time.DateFormatUtils;

public class Employee {

    private String name;

    private int age;

    private Date dateJoined;

    private String grade;

    private double salary;

    public Employee() {

    }

    public Employee(String name, int age, Date dateJoined, Stringgrade, double salary) {

        this .name = name;

        this .age = age;

        this .dateJoined = dateJoined;

        this .grade = grade;

        this .salary = salary;

    }

    public String toString() {

        return new ToStringBuilder( this , ToStringStyle.SHORT_PREFIX_STYLE)

                .append( "name" ,name)

                .append( "age" ,age)

                .append( "dateJoined" ,DateFormatUtils.format(dateJoined, "yyyy-MM-dd" ))

                .append( "grade" ,grade)

                .append( "salary" ,salary)

                .toString();

    }

    public int getAge() {

        return age;

    }

    public void setAge( int age) {

        this .age = age;

    }

    public Date getDateJoined() {

        return dateJoined;

    }

    public void setDateJoined(Date dateJoined) {

        this .dateJoined = dateJoined;

    }

    public String getGrade() {

        return grade;

    }

    public void setGrade(String grade) {

        this .grade = grade;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this .name = name;

    }

    public double getSalary() {

        return salary;

    }

    public void setSalary( double salary) {

        this .salary = salary;

    }

}

/**TransformerUsage.java */

package sean.study.commons.collections;

import java.util.Arrays;

import java.util.Collection;

import java.util.Date;

import java.util.Iterator;

import java.util.List;

import org.apache.commons.collections.CollectionUtils;

import org.apache.commons.collections.Predicate;

import org.apache.commons.collections.Transformer;

import org.apache.commons.collections.functors.SwitchTransformer;

import org.apache.commons.lang.StringUtils;

public class TransformerUsage {

    public static void main(String[] args) {

        demoTransformerUsage();

    }

    public static void demoTransformerUsage() {

        System.out.println(StringUtils.center( " demoTransformerUsage " , 40, "=" ));

        // data setup

        Applicant[] applicants = new Applicant[] {

            new Applicant( "Tony" ,26, "Developer" ),

            new Applicant( "Michelle" ,24, "Tester" ),

            new Applicant( "Jack" ,28, "Project Manager" )

        };

        List appList =Arrays.asList(applicants);

        // predicate setup

        Predicate isDeveloper = new Predicate() {

            public boolean evaluate(Object obj) {

                Applicant app = (Applicant) obj;

                return "Developer" .equalsIgnoreCase(app.getApplyFor());

            }

        };

        Predicate isTester = new Predicate() {

            public boolean evaluate(Object obj) {

                Applicant app = (Applicant) obj;

                return "Tester" .equalsIgnoreCase(app.getApplyFor());

            }

        };

        Predicate isPM = new Predicate() {

            public boolean evaluate(Object obj) {

                Applicant app = (Applicant) obj;

                return "ProjectManager" .equalsIgnoreCase(app.getApplyFor());

            }

        };

        Predicate[] checkApplyFor = new Predicate[] {

            isDeveloper,

            isTester,

            isPM

        };

        // transformer setup

        Transformer developerTransformer = new Transformer() {

            public Object transform(Object obj) {

                Applicantapp = (Applicant) obj;

                return new Employee(

                    app.getName(), app.getAge(), new Date(), "E4" , 2000

                );

            }

        };

        Transformer testerTransformer = new Transformer() {

            public Object transform(Object obj) {

                Applicant app = (Applicant) obj;

                return new Employee(

                    app.getName(), app.getAge(), new Date(), "E4" , 2000

                );

            }

        };

        Transformer pmTransformer = new Transformer() {

            public Object transform(Object obj) {

                Applicant app = (Applicant) obj;

                return new Employee(

                    app.getName(), app.getAge(), new Date(), "E5" , 3000

                );

            }

        };

        Transformer[] transformers = new Transformer[] {

            developerTransformer,

            testerTransformer,

            pmTransformer

        };

        // transform

        Transformer employTransformer = new SwitchTransformer(

            checkApplyFor, transformers, null

        );

        Collection employed =CollectionUtils.collect(appList, employTransformer);

        // output

        System.out.println( "Applicants: " );

        Iterator iter1 = appList.iterator();

        while (iter1.hasNext()) {

            System.out.println(iter1.next());

        }

        System.out.println( "Employed: " );

        Iterator iter2 = employed.iterator();

        while (iter2.hasNext()) {

            System.out.println(iter2.next());

        }

        System.out.println(StringUtils.repeat( "=" , 40));

    }

}

以下是運行結果:

=========demoTransformerUsage =========

Applicants:

Applicant[name=Tony,age=26,applyFor=Developer]

Applicant[name=Michelle,age=24,applyFor=Tester]

Applicant[name=Jack,age=28,applyFor=ProjectManager]

Employed:

Employee[name=Tony,age=26,dateJoined=2005-08-05,grade=E4,salary=2000.0]

Employee[name=Michelle,age=24,dateJoined=2005-08-05,grade=E4,salary=2000.0]

Employee[name=Jack,age=28,dateJoined=2005-08-05,grade=E5,salary=3000.0]

========================================

我們首先定義一組Predicate,用於在SwitchTransformer中判斷採用那個具體的Transformer,這個具體的Transformer也是通過數組同時傳遞給SwitchTransformer的構造方法的。不同的Predicate可以有不同的實現,不同的Transformer也可以有不同的實現,因爲它們之間實際上完全是相互獨立的。這就使我們有效的分離了邏輯和具體業務。

1.5.8.            Closure組

Closure組。

Closure

ChainedClosure

IfClosure

WhileClosure

ClosureUtils

Closure這一組接口和類提供一個操作對象的execute方法,爲我們在處理一系列對象時可以將處理邏輯分離出來。理論上講,使用Transformer也可以達到類似的效果,只要輸出對象和輸入對象是同一個對象就好,但是Closure接口定義的execute方法返回void,並且從效果和功能區分上,Closure可以更好的詮釋對象處理或執行的意思。而事實上,ClosureUtils中也提供了一個asClosure方法包裝一個現成的Transformer。

沿用前面的Emploee類,我們來給一組員工漲工資:

package sean.study.commons.collections;

import java.util.Arrays;

import java.util.Collection;

import java.util.Date;

import java.util.Iterator;

import org.apache.commons.collections.Closure;

import org.apache.commons.collections.CollectionUtils;

import org.apache.commons.lang.StringUtils;

public class ClosureUsage {

    public static void main(String[] args) {

        demoClosureUsage();

    }

    public static void demoClosureUsage() {

        System.out.println(StringUtils.center( " demoClosureUsage " , 40, "=" ));

        // data setup

        Employee[] employees = new Employee[] {

            new Employee( "Tony" ,26, new Date(), "E4" , 2000),

            new Employee( "Michelle" ,24, new Date(), "E4" , 2000),

            new Employee( "Jack" ,28, new Date(), "E5" , 3000)

        };

        Collection empColl =Arrays.asList(employees);

        printColl( "Beforesalary increase:" , empColl);

        // closure setup

        Closure salaryIncreaseClosure = new Closure() {

            public void execute(Object obj) {

                Employee emp = (Employee) obj;

                emp.setSalary(emp.getSalary() *1.20);

            }

        };

        // salary increase

        CollectionUtils.forAllDo(empColl,salaryIncreaseClosure);

        printColl( "Aftersalary increase:" , empColl);

        System.out.println(StringUtils.repeat( "=" , 40));

    }

    public static void printColl(String label, Collection c) {

        if (StringUtils.isNotBlank(label)) {

            System.out.println(label);

        }

        Iterator iter = c.iterator();

        while (iter.hasNext()) {

            System.out.println(iter.next());

        }

    }

}

以下是運行結果:

===========demoClosureUsage ===========

Before salaryincrease:

Employee[name=Tony,age=26,dateJoined=2005-08-05,grade=E4,salary=2000.0]

Employee[name=Michelle,age=24,dateJoined=2005-08-05,grade=E4,salary=2000.0]

Employee[name=Jack,age=28,dateJoined=2005-08-05,grade=E5,salary=3000.0]

After salaryincrease:

Employee[name=Tony,age=26,dateJoined=2005-08-05,grade=E4,salary=2400.0]

Employee[name=Michelle,age=24,dateJoined=2005-08-05,grade=E4,salary=2400.0]

Employee[name=Jack,age=28,dateJoined=2005-08-05,grade=E5,salary=3600.0]

========================================

我這裏舉的是一個相對簡單的例子,在Closure這一組還有一些很方便的類,如ChainedClosure可以包裝一組Closure作爲整體執行;IfClosure在創建時需要提供給它一個Predicate和兩個Closure,執行時先做Predicate判定再決定執行哪一個Closure;SwitchClosure跟SwitchTransformer類似,根據創建時傳入的Predicate組和Closure組對應執行;WhileClosure則根據創建時傳入的Predicate做判斷,如果爲true則執行Closure,直到Predicate返回false;等等。

具體用法請參考Javadoc。

1.5.9.            Iterator組

來看最後一組 – Iterator。

LoopingIterator

ArrayListIterator

FilterIterator

UniqueFilterIterator

IteratorUtils

java.util.Iterator接口定義了標準的Collection遍歷方法,但是如果不做改變的使用它,我們得到的是從頭到尾一次性的遍歷。假如我們需要循環遍歷,假如我們需要遍歷某一段,假如我們需要遍歷滿足某些條件的元素,等等等等,我們就不能完全依賴於這個Iterator的標準實現了。除非我們寧可在此基礎上在調用的代碼中多加一些判斷,不過這樣的話代碼就會顯得混亂,時間長了就容易變得難以維護。Commons Collections的這一組Iterator爲我們帶來了便利。

這些Iterator使用都很一目瞭然,直接看例子吧:

package sean.study.commons.collections;

import java.util.Arrays;

import java.util.Iterator;

import java.util.List;

import org.apache.commons.collections.Predicate;

import org.apache.commons.collections.iterators.ArrayListIterator;

import org.apache.commons.collections.iterators.FilterIterator;

import org.apache.commons.collections.iterators.LoopingIterator;

import org.apache.commons.lang.StringUtils;

public class IteratorUsage {

    public static void main(String[] args) {

        demoIteratorUsage();

    }

    public static void demoIteratorUsage() {

        System.out.println(StringUtils.center( " demoClosureUsage " , 40, "=" ));

        // data setup

        String[] weekDays = {

            "Monday" , "Tuesday" , "Wednesday" ,

            "Thursday" , "Friday" , "Saturday" , "Sunday"

        };

        List weekDayList =Arrays.asList(weekDays);

        // workdays

        Iterator iter1 = new ArrayListIterator(weekDays, 0,5);

        printColl( "Partial:" ,iter1, 5);

        // loop

        Iterator iter2 = new LoopingIterator(weekDayList);

        printColl( "Loop:" , iter2, 10);

        // looping workdays

        Predicate notWeekendPredicate = new Predicate() {

            public boolean evaluate(Object obj) {

                String str = (String) obj;

                if ( "Saturday" .equalsIgnoreCase(str)){

                    return false;

                }

                if ( "Sunday" .equalsIgnoreCase(str)){

                    return false;

                }

                return true;

            }

        };

        Iterator iter3 = new FilterIterator(

            new LoopingIterator(weekDayList),

            notWeekendPredicate

        );

        printColl( "NoWeekends loop:" , iter3, 12);

        System.out.println(StringUtils.repeat( "=" , 40));

    }

    public static void printColl(String label, Iterator iter, int maxCount) {

        if (StringUtils.isNotBlank(label)) {

            System.out.println(label);

        }

        int i = 0;

        while (iter.hasNext() && i < maxCount){

            System.out.println( "# " + iter.next() + " #" );

            i++;

        }

    }

}

運行結果如下:

===========demoClosureUsage ===========

Partial:

# Monday #

# Tuesday #

# Wednesday #

# Thursday #

# Friday #

Loop :

# Monday #

# Tuesday #

# Wednesday #

# Thursday #

# Friday #

# Saturday #

# Sunday #

# Monday #

# Tuesday #

# Wednesday #

No Weekends loop:

# Monday #

# Tuesday #

# Wednesday #

# Thursday #

# Friday #

# Monday #

# Tuesday #

# Wednesday #

# Thursday #

# Friday #

# Monday #

# Tuesday #

========================================

有了這些實用的Iterator類,我們就可以輕鬆的實現可配置的遍歷行爲了。

1.6. 結語

在前面的隨筆中,我們一起過了一遍Jakarta Commons這個類庫中非常重要的三個子項目:Commons Lang、Commons BeanUtils和Commons Collections,這些工具包在無數開源或商業框架中都可以找到,可以說應用範圍非常廣。

當然,Jakarta Commons提供的API遠不止我們提到的這些,除了上述三個核心項目之外,還有讀取和映射XML文檔的Digester/Betwixt、處理命令行參數的CLI、提供常用編碼算法的Codec、用於讀取多種格式的配置文件的Configuration、發送電子郵件的Email、上傳文件的FileUpload、模擬HTTP客戶端的HttpClient、處理日誌的Logging等等等等,實在是相當的豐富。

Jakarta Commons官網上可以找到完整的組件列表:

http://jakarta.apache.org/commons/

今後如果發現特別有價值需要跟大家分享的,我還會貼出來,只是不會像這樣有條理和規律了。希望我的這一組筆記對大家瞭解和認識Jakarta Commons有所幫助,也希望這些優秀的類庫及其源碼能夠給大家帶來工作效率和編程功底上的提升。

我整理了一份清單,列出了所有這一系列隨筆的鏈接,方便大家查找和閱讀
發佈了0 篇原創文章 · 獲贊 8 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章