1. 程式人生 > >【Java】java.util.Objects 工具類方法研究

【Java】java.util.Objects 工具類方法研究



Objects 與 Object 區別

Object 是 Java 中所有類的基類,位於java.lang包。 Objects 是 Object 的工具類,位於java.util包。它從jdk1.7開始才出現,被final修飾不能被繼承,擁有私有的建構函式。 它由一些靜態的實用方法組成,這些方法是null-save(空指標安全的)或null-tolerant(容忍空指標的),用於計算物件的hashcode、返回物件的字串表示形式、比較兩個物件。

Objects 各方法介紹與分析

equals

equals方法是判斷兩個物件是否相等。 在比較兩個物件的時候,Object.equals方法容易丟擲空指標異常。 ——我剛上班的時候,有位老員工教我“字串常量與變數物件比較的時候,常量要寫在equals外邊,變數放在equals()括號裡邊。” 就是這個原因。 如果是兩個變數比較的時候,就都需要加非空判斷。 * Object.equals方法內呼叫的是return
(this== obj)。String類中是依據字串內容是否相等來重定義了equals方法。

現在,Objects.equals方法中已經做了非空判斷,所以不會丟擲空指標異常,它是null-save空指標安全的,而且也可以簡化程式碼。

原始碼如下:

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

deepEquals

顧名思義,深度比較兩個物件。 當引數是陣列物件,其方法內部採用的是Arrays.deepEquals0方法的演算法。 使用Objects.deepEquals方法有個好處,當我們在寫業務程式碼時,可以直接使用此方法來判斷兩個複雜型別, 比如使用了泛型的列表物件List<T>、或者通過反射得到的物件,不清楚物件的具體型別。 原始碼如下:
 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);
    }

 簡短的說明下Arrays.deepEquals0方法:

     如果引數是Object型別的陣列,則呼叫Arrays.deepEquals方法,在引數陣列的迴圈中,遞迴呼叫deepEquals0,直到出現不相同的元素,或者迴圈結束;      如果引數是基本型別的陣列,則根據該型別呼叫Arrays.equals方法。Arrays工具類依照八種基本型別對equals方法做了過載。

返回目錄

hashCode

返回一個整型數值,表示該物件的雜湊碼值。若引數物件為空,則返回整數0;若不為空,則直接呼叫了Object.hashCode方法。 原始碼如下:
public static int hashCode(Object o) {
        return o != null ? o.hashCode() : 0;
    }

Object支援hashCode方法是為了提高雜湊表(例如java.util.Hashtable 提供的雜湊表)的效能。

以集合Set為例,當新加一個物件時,需要判斷現有集合中是否已經存在與此物件相等的物件,如果沒有hashCode()方法,需要將Set進行一次遍歷,並逐一用equals()方法判斷兩個物件是否相等,此種演算法時間複雜度為o(n)。通過藉助於hasCode方法,先計算出即將新加入物件的雜湊碼,然後根據雜湊演算法計算出此物件的位置,直接判斷此位置上是否已有物件即可。 (注:Set的底層用的是Map的原理實現)

返回目錄

hash

為一系列的輸入值生成雜湊碼,該方法的引數是可變引數。 原始碼如下:
public static int hash(Object... values) {
        return Arrays.hashCode(values);
    }

它是將所有的輸入值都放到一個數組,然後呼叫Arrays.hashCode(Object[])方法來實現雜湊碼的生成。 對於當一個物件包含多個成員,重寫Object.hashCode方法時,hash方法非常有用。 舉個Java原始碼中的例子: java.lang.invoke.MemberName 類,該類有Class<?> clazz、String name、Object type、int flags、Object resoulution這幾個成員變數, 該類的hashCode方法如下:
 @Override
    public int hashCode() {
        return Objects.hash(clazz, getReferenceKind(), name, getType());
    }

 警告:當提供的引數是一個物件的引用,返回值不等於該物件引用的雜湊碼。這個值可以通過呼叫hashCode方法來計算。

toString

  toString(Object o)

  返回指定物件的字串表示形式。如果引數為空物件null,則返回字串“null”。   該方法內部呼叫的是     returnString.valueOf(o);   String.valueOf(Object obj)方法的內部實現為     return(obj==null) ?"null":obj.toString();   Object.toString()方法的內部實現為     returngetClass().getName() +"@"+ Integer.toHexString(hashCode());

  toString(Object o, String nullDefault)

返回指定物件的字串表示形式。如果引數為空物件null,則返回第二個引數nullDefault所指定的物件。

返回目錄

compare

如果兩個引數相同則返回整數0。因此,如果兩個引數都為空物件null,也是返回整數0。 注意:如果其中一個引數是空物件null,是否會丟擲空指標異常NullPointerException取決於排序策略,如果有的話,則由Comparator來決定空值null。 原始碼如下:

返回目錄

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

 

requireNonNull

  requireNonNull(T obj)

檢查指定型別的物件引用不為空null。當引數為null時,丟擲空指標異常。設計這個方法主要是為了在方法、建構函式中做引數校驗。

  原始碼如下:

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

  舉個例子:

  當我們通過帶參的建構函式建立物件時,建立物件的同時就可以進行引數校驗。同時也簡化了很多程式碼。
 public class Foo {
          public Foo(Bar bar) {
               this.bar = Objects.requireNonNull(bar);
          }
     }

 

  requireNonNull(T obj, String message) 

  該方法是requireNonNull的過載方法,當被校驗的引數為null時,根據第二個引數message丟擲自定義的異常訊息。   原始碼如下:
 public static <T> T requireNonNull(T obj, String message) {
        if (obj == null)
            throw new NullPointerException(message);
        return obj;
    }

  requireNonNull(T obj, Supplier<String> messageSupplier)

  檢查指定的物件引用不為空null,如果是空,丟擲自定義的空指標異常。從jdk1.8開始。   與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;
    }

 

isNull

判空方法,如果引數為空則返回true。從jdk1.8開始。

原始碼如下:

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

apiNote: 該方法的存在是用於java.util.function.Predicate類,filter(Objects::isNull)。 來看下Predicate類中,使用到本方法的程式碼: 
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)  
                ? Objects::isNull   // 雙冒號,代表方法引用。
                : object -> targetRef.equals(object); // 此處為lambda表示式。接收object物件,返回引數targetRef與該物件的比較結果。
    }

nonNull

判斷非空方法,如果引數不為空則返回true。從jdk1.8開始。

原始碼如下:

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

apiNote: 該方法的存在是用於java.util.function.Predicate類,filter(Objects::nonNull)。