Java 基礎之 Object
方法摘要
public final native Class<?> getClass() //獲取物件的執行時物件的類 public native int hashCode() //獲取物件的 hash 值 public boolean equals(Object obj) //比較兩個物件是否相等 protected native Object clone() //建立並返回一個物件的拷貝 public String toString() //返回物件的字串表示形式。 public final native void notify() //喚醒在該物件上等待的某個執行緒 public final native void notifyAll() //喚醒在該物件上等待的所有執行緒 public final void wait() //讓當前執行緒進入等待狀態。直到其他執行緒呼叫此物件的 notify() 方法或 notifyAll() 方法。 public final native void wait(long timeout) //讓當前執行緒處於等待(阻塞)狀態,直到其他執行緒呼叫此物件的 notify() 方法或 notifyAll() 方法,或者超過引數設定的timeout超時時間。 public final void wait(long timeout, int nanos) //與 wait(long timeout) 方法類似,多了一個 nanos 引數,這個引數表示額外時間(以納秒為單位,範圍是 0-999999)。 所以超時的時間還需要加上 nanos 納秒。。 protected void finalize() //當 GC (垃圾回收器)確定不存在對該物件的有更多引用時,由物件的垃圾回收器呼叫此方法。
equals()
等價關係
- 自反性
x.equals(x) //true
- 對稱性
x.equals(y) == y.equals(x) //true
- 傳遞性
x.equals(y) //true
y.equals(z) //true
x.equals(z) //true
- 一致性
多次呼叫 equals() 方法結果不變
x.equals(y) == x.equals(y); // true
- 與 null 的比較
對任何不是 null 的物件 x 呼叫 x.equals(null) 結果都為 false
x.equals(null); // false;
等價與相等
- 對於基本型別,==判斷兩個值是否相等,基本型別沒有 equals() 方法
- 對於引用型別,==判斷兩個值的引用是否相等,而 equals() 方法判斷引用的物件是否相等
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y); // false
實現
- 檢查是否為同一個物件的引用,如果是直接返回 true;
- 檢查是否是同一個型別,如果不是,直接返回 false;
- 將 Object 物件進行轉型;
- 判斷每個關鍵域是否相等。
public class EqualExample { private int x; private int y; private int z; public EqualExample(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EqualExample that = (EqualExample) o; if (x != that.x) return false; if (y != that.y) return false; return z == that.z; } }
hashCode()
hashCode 用於獲取物件的 hash 值,表示在雜湊表中的位置。
equals() 方法判斷兩個物件是否等價,等價的兩個物件雜湊值一定相等,而雜湊值相等的兩個物件不一定等價,因為計算雜湊值具有隨機性。
在覆蓋 object 的 equals() 方法時應當總是覆蓋 hashCode() 方法,保證等價的兩個物件雜湊值也是相等的
String 和 ArrayList 類都重寫了 Object 的 equals() 方法,並重寫了 hashCode() 方法
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
}
重寫 hashCode 方法時,需要用素數31作為乘子,因為31可以被 JVM 優化
- 左移 << : 左邊的最高位丟棄,右邊補全0(把 << 左邊的資料*2的移動次冪)。
- 右移 >> : 把>>左邊的資料/2的移動次冪。
- 無符號右移 >>> : 無論最高位是0還是1,左邊補齊0。
所以 :31 * i = (i << 5) - i(左邊 31 * 2=62,右邊2 * 2^5-2=62)兩邊相等,JVM就可以高效的進行計算
toString
toString() 方法用於返回物件的字串表示形式。預設返回格式:物件的 class 名稱 + @ + hashCode 的十六進位制字串。
public static void main(String[] args) {
A a = new A();
System.out.println(a.toString()); //client.A@b4c966a
}
clone()
1. cloneable
clone() 是 Object 的 protected 方法,它不是 public,一個類不顯式去重寫 clone(),其它類就不能直接去呼叫該類例項的 clone() 方法。
public class A {
public static void main(String[] args) {
A a = new A();
a.clone(); //Unhandled exception: java.lang.CloneNotSupportedException
}
}
重寫 clone() 方法,必須實現 Cloneable,因為 Cloneable 介面規定,如果一個類沒有實現 Cloneable 介面又呼叫了 clone() 方法,就會丟擲 java.lang.CloneNotSupportedException
public class A implements Cloneable {
private int a;
private int b;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A();
a.clone();
}
}
2. 淺拷貝
拷貝物件和原始物件的引用型別引用同一個物件。
public class A implements Cloneable {
private int arr;
public int getArr() {
return arr;
}
public void setArr(int arr) {
this.arr = arr;
}
@Override
public A clone() throws CloneNotSupportedException {
return (A) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A();
a.setArr(123);
A a1 = null;
a1 = a.clone();
System.out.println(a1.getArr()); //123
}
}
3. 深拷貝
拷貝物件和原始物件的引用型別引用不同物件。
public class A implements Cloneable {
private int arr;
public int getArr() {
return arr;
}
public void setArr(int arr) {
this.arr = arr;
}
@Override
public A clone() throws CloneNotSupportedException {
A result = (A) super.clone();
result.arr = this.arr;
return result;
}
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A();
a.setArr(123);
A a1 = null;
a1 = a.clone();
System.out.println(a1.getArr()); //123
}
}
4. clone() 的替代方案
使用 clone() 方法來拷貝一個物件即複雜又有風險,它會丟擲異常,並且還需要型別轉換。Effective Java 書上講到,最好不要去使用 clone(),可以使用拷貝建構函式或者拷貝工廠來拷貝一個物件。
public class A {
private int arr;
public int getArr() {
return arr;
}
public void setArr(int arr) {
this.arr = arr;
}
public A() {
}
public A(A a) {
this.arr = a.arr;
}
public static void main(String[] args){
A a = new A();
a.setArr(123);
A a1 = new A(a);
System.out.println(a1.getArr()); //123
}
}