一、概述
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覺淺】,獲取第一時間更新哦!