Java中的Object類 (下篇)
要麼讀書,要麼旅行,身體和心靈總有一個要在路上。——羅馬假日
上篇我們講了hashCode和equals方法,首先我們先回顧一下
- hashCode是根據一定的規則和物件相關的資訊對映成一個數值,這個數值成為雜湊值。他是由native關鍵字修飾的,native關鍵字主要是Java平臺與本地C程式碼進行互動的API,即為Java Native Interface(JNI)。
- equals關鍵字是用來比較物件是否相等的。如果使用Object的equals方法,實際上比較的是兩個物件的記憶體地址是否相同,這與我們的初衷往往不一樣。我們常見的型別,如String,Integer,Long等,JDK都為我們重寫了equals方法,所以我們可以直接使用equals方法來實現物件數值的比較。如果是我們自定義的物件,就要手動重寫equals方法。
如有不明白,可以看我另外一篇文章哈。傳送門來了,Java中的Object類 (上篇)。
今天我講clone方法,他的作用就是快速建立一個已有物件的副本,克隆後的物件型別與被克隆物件的型別相同。
咱先簡單使用:
public class Person implements Cloneable { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override protected Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } }
public class Test { public static void main(String[] args){ Person person1=new Person(); person1.setId("1"); person1.setName("張三"); System.out.println("修改前的person1:"+person1.getId()+","+person1.getName()); try { Person person2=person1.clone(); System.out.println("修改前的person2:"+person2.getId()+","+person2.getName()); person2.setId("2"); person2.setName("李四"); System.out.println("修改後的person2:"+person2.getId()+","+person2.getName()); System.out.println("修改後的person1:"+person1.getId()+","+person1.getName()); }catch (Exception e){ e.printStackTrace(); } } }
我們來看一下,這些程式碼到底幹了啥呢。
首先Person類重寫了clone方法,同時也丟擲了cloneNotSupportedException這個異常,也就是說這個這個類不支援cloneable介面,就會丟擲異常,這也就解釋了Person實現了Cloneable介面。
其次我們看一下clone方法裡面,也就是呼叫了父類Object的clone方法。
最後我們看一下測試類Test,先定義了一個person1物件,併為其賦值,id為1,name為張三,這第一行輸出沒啥問題。然後呼叫Person類的clone方法,實現賦值的功能,產生了person2物件,第二行輸出也沒問題。再修改了person2物件,id為2,name為李四,第三行也就輸出了修改後的值。最後輸出了person1物件的值。
這整個過程看下來,clone在這個過程中實現了深克隆,也就是person1和person2是兩個完全不同的物件,他們互不干擾。
咱來試試複雜一點的,看程式碼咯
public class Clothes {
private String color;
private String size;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public Clothes(String color, String size) {
this.color = color;
this.size = size;
}
}
public class Person implements Cloneable {
private String name;
private Clothes clothes;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Clothes getClothes() {
return clothes;
}
public void setClothes(Clothes clothes) {
this.clothes = clothes;
}
public Person(String name, Clothes clothes) {
this.name = name;
this.clothes = clothes;
}
@Override
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person("張三", new Clothes("紅", "m號"));
System.out.println("修改前的person1:name("+person1.getName()+"),clothes("+person1.getClothes().getColor()+","+person1.getClothes().getSize()+")");
try {
Person person2 = (Person) person1.clone();
System.out.println("修改前的person2:name("+person2.getName()+"),clothes("+person2.getClothes().getColor()+","+person2.getClothes().getSize()+")");
person2.setName("李四");
person2.getClothes().setColor("藍");
person2.getClothes().setSize("l號");
System.out.println("修改後的person2:name("+person2.getName()+"),clothes("+person2.getClothes().getColor()+","+person2.getClothes().getSize()+")");
System.out.println("修改後的person1:name("+person1.getName()+"),clothes("+person1.getClothes().getColor()+","+person1.getClothes().getSize()+")");
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
這幾個類描述了一個person類和clothes類,學生類中包括成員變數name和clothes。person類中重寫了clone()方法,而clothes類並沒有重寫clone方法。
我們看一下執行結果,person2是由person1克隆過來的,所以輸出語句的第一行和第二行,屬性值是一樣的。然後修改了person2的name值和clothes值,結果person1的name值沒有改變,還是原來的張三,而clothes值並變成了藍,l號。
這說明其實呼叫Object類的clone方法,是在記憶體上開闢一塊和原始物件一樣的空間,然後原樣拷貝原始物件的內容,對於基本的資料型別來說,是沒有物件的(就像剛才的示例一樣,在修改完person2的name和clothes後,person1的name並沒有改變,還是原來的張三)。但對於非基礎型別來說,他儲存的只是物件的引用(就像剛才的示例,在修改完person2的name和clothes後,person1的clothes發生了變化)。
解決方法:
將非基礎型別也重寫clone方法,實現引用克隆。至於程式碼,就在上面Clothes類裡面加上重寫的程式碼就ok啦,我就不寫了哦。