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表示式和函數語言程式設計服務的。
【總結】:這個是一個輕量級的工具類,擁有很多很實用的小方法。值得推薦。