一、源碼
/* 通過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(),可以使用拷貝構造函數或者拷貝工廠來拷貝一個對象。