我的jdk源碼(十七):Objects類

一、概述

    Objects類是一Object類的個工具類,類似Collections類,提供一些靜態的工具方法,着重於校驗空指針以及獲取hash值等,彌補我們在寫代碼時不小心忽略空指針等異常情況,從jdk1.7被加入進來, Objects類被final修飾不能被繼承,擁有私有的構造函數。

二、源碼解析

    1. 類的聲明

public final class Objects

    沒什麼好說的,就是被final修飾, 表示Objects類爲最終類,不能被繼承 。

    2. 構造方法

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

    不能被實例化,直接拋出異常。

    3. equals()方法

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

    源碼中針對對象a進行了非空判斷,不爲空纔會使用a.equals(b),從而保證不會拋出空指針異常。此處說一下==和equals方法的特點與區別。

   a. ==是對象引用比較

        * 如果比較的對象都是基本數據類型,則比較數據的大小

        * 如果有引用類型,則比較兩個對象的內存地址

        注意:自動拆裝箱有個坑,如下:

Integer n1=128;
Integer n2=128;
n1==n2;

Integer n3=127;
Integer n4=127;
n3==n4;

    Integer包裝類型中存在自動裝箱的情況,當數據範圍在-128~127之間時,創建的對象會在方法區的常量池中開闢空間(可複用),數據超出範圍就會在堆區中開闢空間,由於指向不同對象所以n1==n2判等的結果爲false,n3與n4指向常量池同一地址所以判等結果爲true。

    b. equals()方法是邏輯比較

        * 一般情況下是調用Object類的equals()方法,比較兩個對象是否是同一個對象

        * 特殊類比如String等重寫equals()方法後,要根據自己定義的規則進行比較判斷。

    4. deepEquals()方法

    public static boolean deepEquals(Object a, Object b) {
        //如果是同一個對象,直接返回true
        if (a == b)
            return true;
        //只要有一個對象爲null,直接返回false,也就是說,即使兩個對象都爲null,也返回false
        else if (a == null || b == null)
            return false;
        else
            //調用Arrays類的deepEquals0()方法進行深度判斷
            return Arrays.deepEquals0(a, b);
    }

    //Arrays類的deepEquals0()方法,可以看出源碼根據不同的類型進行了單獨的邏輯處理
    static boolean deepEquals0(Object e1, Object e2) {
        assert e1 != null;
        boolean eq;
        //如果兩個對象都是數組類型,則調用deepEquals()方法
        if (e1 instanceof Object[] && e2 instanceof Object[])
            eq = deepEquals ((Object[]) e1, (Object[]) e2);
        else if (e1 instanceof byte[] && e2 instanceof byte[])
            eq = equals((byte[]) e1, (byte[]) e2);
        else if (e1 instanceof short[] && e2 instanceof short[])
            eq = equals((short[]) e1, (short[]) e2);
        else if (e1 instanceof int[] && e2 instanceof int[])
            eq = equals((int[]) e1, (int[]) e2);
        else if (e1 instanceof long[] && e2 instanceof long[])
            eq = equals((long[]) e1, (long[]) e2);
        else if (e1 instanceof char[] && e2 instanceof char[])
            eq = equals((char[]) e1, (char[]) e2);
        else if (e1 instanceof float[] && e2 instanceof float[])
            eq = equals((float[]) e1, (float[]) e2);
        else if (e1 instanceof double[] && e2 instanceof double[])
            eq = equals((double[]) e1, (double[]) e2);
        else if (e1 instanceof boolean[] && e2 instanceof boolean[])
            eq = equals((boolean[]) e1, (boolean[]) e2);
        else
            eq = e1.equals(e2);
        return eq;
    }

    //用於數組的嵌套調用比較兩個數組元素是否相同
    public static boolean deepEquals(Object[] a1, Object[] a2) {
        if (a1 == a2)
            return true;
        if (a1 == null || a2==null)
            return false;
        int length = a1.length;
        if (a2.length != length)
            return false;

        for (int i = 0; i < length; i++) {
            Object e1 = a1[i];
            Object e2 = a2[i];

            if (e1 == e2)
                continue;
            if (e1 == null)
                return false;

            // Figure out whether the two elements are equal
            boolean eq = deepEquals0(e1, e2);

            if (!eq)
                return false;
        }
        return true;
    }

    由源碼我們可以看出,如果參數是Object類型的數組,則調用Arrays.deepEquals方法,在參數數組的循環中,遞歸調用deepEquals0,直到出現不相同的元素,或者循環結束;如果參數是基本類型的數組,則根據該類型調用Arrays.equals方法。Arrays工具類依照八種基本類型對equals方法做了重載。

    5. hashCode()方法

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

    如果對象o不爲null,則調用Object類的hashCode()犯法返回其hash值,否則返回0。這裏也是做了空指針安全的處理,因爲如果直接調用null的hashCode()則會拋出異常,而不是返回0。

    6. hash()方法

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

    //Arrays類的hashCode的方法
    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;
    }

    Objects類hash()方法是調用了Arrays類的hashCode()方法,傳入的所有參數對象都放進一個values數組中。Arrays類的hashCode()方法是循環遍歷所有的元素進行了計算,可以看出,返回的hash值並不是所有元素的hashcode值的總和,並且即使數組中僅有一個元素,返回的hash值也不是這個元素本身的hash值。

    7. toString系列方法

    //調用String類的valueOf()方法,即使對象爲null,也能返回字符串"null"
    public static String toString(Object o) {
        return String.valueOf(o);
    }

    // 可以指定對象爲null時,返回默認的字符串。如果不爲null,則調用Object類的toString()方法
    public static String toString(Object o, String nullDefault) {
        return (o != null) ? o.toString() : nullDefault;
    }

    //String類中的valueOf()方法
    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

    //Object類的toString()方法
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    8. compare()方法

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

    先判斷ab對象是否相同,就算都爲null,也能返回整數0,不相同再調用比較器實例c的內部比較規則。

    9. requireNonNull系列方法

    //檢查指定類型的對象引用不爲空null。當參數爲null時,拋出空指針異常。設計這個方法主要是爲了在方法、構造函數中做參數校驗。
    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

    //此方法是requireNonNull的重載方法,當被校驗的參數爲null時,根據第二個參數message拋出自定義的異常消息。
    public static <T> T requireNonNull(T obj, String message) {
        if (obj == null)
            throw new NullPointerException(message);
        return obj;
    }
 
    //檢查指定的對象引用不爲空null,如果是空,拋出自定義的空指針異常。
    //與requireNonNull(Object, String)方法不同,本方法允許將消息的創建延遲,直到空檢查結束之後。 雖然在非空例子中這可能會帶來性能優勢, 但是決定調用本方法時應該小心,創建message supplier的開銷低於直接創建字符串消息。
    public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
        if (obj == null)
            throw new NullPointerException(messageSupplier.get());
        return obj;
    }

    requireNonNull()方法的設計是爲了在方法、構造函數中做參數校驗,當我們通過帶參的構造函數創建對象時,創建對象的同時就可以進行參數校驗。同時也簡化了很多代碼。

    10. isNull()方法

    public static boolean isNull(Object obj) {
        return obj == null;
    }

    判斷參數是否爲null,如果是,則返回true。

    11. nonNull()方法

    public static boolean nonNull(Object obj) {
        return obj != null;
    }

    判斷參數是否不爲null,如果是,則返回true。

三、總結

    總的來說Objects類提供了不少便利的方法,可以讓我們少寫一些非空校驗,大家可以靈活的運用到我們的日常代碼中。 敬請期待《 我的jdk源碼(十八): LinkedHashSet 》。

    更多精彩內容,敬請掃描下方二維碼,關注我的微信公衆號【Java覺淺】,獲取第一時間更新哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章