1. 程式人生 > >一篇關於apache commons類庫的詳解

一篇關於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的最新動態和詳細資訊,可以參考:

1.2.簡介

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,下載地址如下:

其中後一個是原始碼。

這一組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:[email protected][

  staffId=123

  staffName=John Smith

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

]

staff2's info:[email protected][

  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包。對後者感興趣的可以看看

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檔案;

Commons BeanUtils的原始碼下載地址:

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都有)

目前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中,不狹義的區分語法上的介面和類,把它們都看作是類的話,大致我們可以發現三種主要的類