1. 程式人生 > >java原始碼閱讀之java.util.Objects

java原始碼閱讀之java.util.Objects

之所以寫這篇文章,是因為工作中接觸到一個開源專案程式碼,而這個開原始碼使用到了這個類。同時如果不是前面的包名java.util,都很容易看錯成java超類java.lang.Object。

java.util.Objects是java1.7新增的一個類。下面這篇文章將基於1.7.0_80版本的類庫原始碼展開。

一、類定義:

package java.util;

/**
 * @since 1.7
 */
public final class Objects {

我去掉了類上面的註釋,只保留了一個註解標籤,用來記住這個類是自1.7開始出現的。另外需要注意的是這個類用關鍵字final修飾了,意味著它不能被繼承

二、建構函式:

    private Objects() {
        throw new AssertionError("No java.util.Objects instances for you!");
    }

只有這麼一個建構函式,並且宣告為private,建構函式中丟擲一個AssertionError。這兩點結合起來看,意味著:這個類不可以例項化。(這裡我們不考慮反射打破這一規則的問題)

綜合三點,(1)java.util包下面的類;(2)不可以被繼承;(3)不能例項化。

可以推測,這個類八九不離十,是個工具類

三、公開的方法:

(1)判斷兩個物件是否相等

public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

引數a和b都是Object型別,意味著這個方法很靈活,能夠接收所有的物件引用。

方法內部也很有意思,用到了短路或,先判斷鏈兩個引用是否指向同一物件。如果不是,再呼叫a引數的equals方法,等於將

是否相等的任務委託給引用a所指向的物件去執行了。同時很細心地,先判斷a是不是null。否則很容易引起NPE問題。

(2)判斷兩個物件是否相等。

  public static boolean deepEquals(Object a, Object b) {
        if (a == b)
            return true;
        else if (a == null || b == null)
            return false;
        else
            return Arrays.deepEquals0(a, b);
    }

不同於(1)中的函式,這個函式,直接指明瞭,如果引數a或者b其中有1個為null,而另一個不為null,那麼就返回false。

等到else判斷的時候,已經可以確定的是a和b都不是null了。同時也將判斷委託給Arrays類的deepEquals0方法了。有興趣的讀者可以去看一下這個方法的具體實現。

(3)獲取物件hashCode值方法:

    public static int hashCode(Object o) {
        return o != null ? o.hashCode() : 0;
    }

方法很簡單,判斷引用o所指物件是否為null,不為null,則返回o所指物件的hasCode方法執行結果,為null,返回0。

(4)還是獲取hash值的方法,並且還是一個變長引數的方法。

    public static int hash(Object... values) {
        return Arrays.hashCode(values);
    }

真正執行方法的是Arrays類的方法:【public static int hashCode(Object a[])】

在這裡也這個方法的具體實現:

    public static int hashCode(Object a[]) {
        if (a == null)
            return 0;

        int result = 1;

        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());

        return result;
    }

細心地讀者可能會發現,變長引數values怎麼可以傳給一個數組型別的方法引數呢?這裡,讀者可以參考我的另一篇文章,我專門講解了變長引數這個語法糖的背後真實實現。

(5)將一個Object引用所指的物件,轉換成一個字串。

 public static String toString(Object o) {
        return String.valueOf(o);
    }

這個方法,仍然是委託給String類去執行,呼叫String的valueOf方法。

其實String的valueOf這個方法也很有意思,有興趣的讀者也可以自行去了解。

(6)將一個Object引用所指的物件,轉換成一個字串。

    public static String toString(Object o, String nullDefault) {
        return (o != null) ? o.toString() : nullDefault;
    }

不同與(5)中方法的是,提供了一個預設值,而且這個預設值是由呼叫者所指定的。

(7)對兩個引用所指物件的比較,使用到了泛型

    public static <T> int compare(T a, T b, Comparator<? super T> c) {
        return (a == b) ? 0 :  c.compare(a, b);
    }

其實這也是個委託實現的方法,重要的第三個引數,是如何實現的compare方法。

(8)  null檢查

  public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

這個是最受我追捧,也是我使用最多的一個方法,它可以提前的探知你的引數是否為null,從而早暴露出NPE問題,並能夠幫助你精確定位。有點類似與guava的Optional類檢查null一樣。

(9)null檢查,但是提供使用者指定的異常資訊。

    public static <T> T requireNonNull(T obj, String message) {
        if (obj == null)
            throw new NullPointerException(message);
        return obj;
    }

三、JDK1.8新增的方法:

新增了三個方法,都是小方法,但也很實用。下面三個方法都是取自JDK1.8.0_151中原始碼。

(1)null判斷,判斷一個Object引用是不是指向null。

     /**
     * @see java.util.function.Predicate
     * @since 1.8
     */
    public static boolean isNull(Object obj) {
        return obj == null;
    }

(2)非null判斷,判斷一個Object引用是否不是指向null。

     /**
     * @see java.util.function.Predicate
     * @since 1.8
     */
    public static boolean nonNull(Object obj) {
        return obj != null;
    }

(3)null檢查

     /**
     * @since 1.8
     */
    public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
        if (obj == null)
            throw new NullPointerException(messageSupplier.get());
        return obj;
    }

可以看出,這三個方法,都是為了適應java1.8引入的lambda表示式和函數語言程式設計服務的。

【總結】:這個是一個輕量級的工具類,擁有很多很實用的小方法。值得推薦。