Object 淺讀

一、源碼

 /* 通過JNI對本地方法進行註冊,映射本地函數與Java方法之間的關係*/
private static native void registerNatives();   

 /* 在對象初始化時自動調用 registerNatives() */    
static {     
    registerNatives();     
}     

/* 返回此 Object 的運行時類*/   
public final native Class<?> getClass()

/*返回該對象的哈希碼值。默認情況下,該方法根據對象的地址來計算哈希碼值*/
public native int hashCode()

/*比較兩個對象是否同一 */  
public boolean equals(Object obj)

/*本地CLONE方法,用於對象的複製 */  
protected native Object clone() throws CloneNotSupportedException

/*返回該對象的字符串表示*/    
public String toString()

/*喚醒在此對象監視器上等待的單個線程*/ 
public final native void notify()

/*喚醒在此對象監視器上等待的所有線程。*/ 
public final native void notifyAll()

/*在其他線程調用此對象的 notify() 方法或 notifyAll() 方法前,導致當前線程等待*/
public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

 /*當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法*/    
protected void finalize() throws Throwable {}

二、hashCode()

hashCode 的通用約定:

  • 在 Java 應用程序執行期間,在對同一對象多次調用 hashCode 方法時,必須一致地返回相同的散列值,前提是將對象進行 equals 比較時所用的信息沒有被修改。從某一應用程序的一次執行到同一應用程序的另一次執行,該整數無需保持一致。
  • 如果根據 equals() 方法,兩個對象是相等的,那麼對這兩個對象中的每個對象調用 hashCode 方法都必須生成相同的散列值。
  • 如果根據 equals(t) 方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用 hashCode 方法不要求一定生成不同的散列值。但是,程序員應該意識到,爲不相等的對象生成不同散列值可以提高哈希表的性能。
  • 當equals方法被重寫時,通常有必要重寫 hashCode 方法,以維護 hashCode 方法的常規協定,該協定聲明相等對象必須具有相等的散列值可。

三、equals()

  • 對於基本類型,== 判斷兩個值是否相等,基本類型沒有 equals() 方法。
  • 對於引用類型,== 判斷兩個變量是否引用同一個對象,而 equals() 判斷引用的對象是否等價。
  • equals() 具有對稱性、自反性、傳遞性。
  • 對任何爲 null 的對象調用 x.equals(null) 結果都爲 false

四、clone()

clone() 有 protected、native 關鍵字修飾,那麼我們需要重寫 Object 的 clone() 實現對象的克隆。

1、淺拷貝

淺拷貝只是複製了對象的引用地址,兩個對象指向同一個內存地址,所以修改其中任意的值,另一個也會隨之變化。

public class Student implements Cloneable {
	// 姓名
	private String name;
	// 年齡
	private int age;
	// 分數
	private Score Score;

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public Student(String name, int age, Score score) {
		super();
		this.name = name;
		this.age = age;
		Score = score;
	}

	public Score getScore() {
		return Score;
	}

	public void setScore(Score score) {
		Score = score;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ",mathematics score=" + Score.getMathematics()
				+ ",chinese score=" + Score.getChinese() + "]";
	}

	// 重寫Object的clone方法
	public Student clone() throws CloneNotSupportedException {
		return (Student) super.clone();
	}

}

class Score {
	private int mathematics;
	private int chinese;

	public Score(int mathematics, int chinese) {
		super();
		this.mathematics = mathematics;
		this.chinese = chinese;
	}

	public int getMathematics() {
		return mathematics;
	}

	public void setMathematics(int mathematics) {
		this.mathematics = mathematics;
	}

	public int getChinese() {
		return chinese;
	}

	public void setChinese(int chinese) {
		this.chinese = chinese;
	}

}

@Test
public static void testShallowClone() throws CloneNotSupportedException {
	Score score = new Score(99, 90);
	Student orgin = new Student("二牛", 22, score);
	Student clone = orgin.clone();
	System.out.println("orgin:" + orgin.toString());
	System.out.println("clone:" + clone.toString());
	orgin.setName("激動");
	orgin.setAge(1);
	score.setMathematics(100);
	score.setChinese(80);
	System.out.println("orgin:" + orgin.toString());
	System.out.println("clone:" + clone.toString());		
}

clone() 對於基本數據類型、String類型(值傳遞)爲深拷貝,對象、數組等引用類型爲淺拷貝。除了重寫 clone() 方法進行淺拷貝,還可以通過拷貝構造方法實現淺拷貝

2、深拷貝

深拷貝是將對象和值都複製過來,兩個對象修改其中任意的值另一個值不會改變。

public class Student implements Cloneable {
	// 姓名
	private String name;
	// 年齡
	private int age;
	// 分數
	private Score Score;

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public Student(String name, int age, Score score) {
		super();
		this.name = name;
		this.age = age;
		Score = score;
	}

	public Score getScore() {
		return Score;
	}

	public void setScore(Score score) {
		Score = score;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ",mathematics score=" + Score.getMathematics()
				+ ",chinese score=" + Score.getChinese() + "]";
	}

	// 重寫Object的clone方法
	public Student clone() throws CloneNotSupportedException {
        // 拷貝 Student 對象
		Student stu = (Student) super.clone();
		// 拷貝 Student 對象 的score屬性
		stu.score = stu.getScore().clone();
		return stu;
	}

}

class Score implements Cloneable {
	private int mathematics;
	private int chinese;

	public Score(int mathematics, int chinese) {
		super();
		this.mathematics = mathematics;
		this.chinese = chinese;
	}

	public int getMathematics() {
		return mathematics;
	}

	public void setMathematics(int mathematics) {
		this.mathematics = mathematics;
	}

	public int getChinese() {
		return chinese;
	}

	public void setChinese(int chinese) {
		this.chinese = chinese;
	}

    // 重寫Object類的clone方法
	public ScoreD clone() throws CloneNotSupportedException {
		return (ScoreD) super.clone();
	}

}

@Test
public static void testShallowClone() throws CloneNotSupportedException {
	Score score = new Score(99, 90);
	Student orgin = new Student("二牛", 22, score);
	Student clone = orgin.clone();
	System.out.println("orgin:" + orgin.toString());
	System.out.println("clone:" + clone.toString());
	orgin.setName("激動");
	orgin.setAge(1);
	score.setMathematics(100);
	score.setChinese(80);
	System.out.println("orgin:" + orgin.toString());
	System.out.println("clone:" + clone.toString());		
}

除了重寫clone方法來實現深拷貝,還可以通過對象序列化實現深拷貝:將對象序列化爲字節序列後,默認會將該對象的整個對象進行序列化,再通過反序列即可實現深拷貝。

cloneable 是一個標記接口,在實現這個接口的情況下,重寫Object中的clone(),然後通過類調用 clone() 才能克隆成功。否則會拋出CloneNotSupportedException。另外,若某個屬性被 transient 關鍵字修飾,那麼該屬性無法被拷貝。

3、clone() 的替代方案

使用 clone() 來拷貝一個對象即複雜又有風險,它會拋出異常,並且需要強制類型轉換。Effective Java 書上講到,最好不要去使用 clone(),可以使用拷貝構造函數或者拷貝工廠來拷貝一個對象。

參考:

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