1.Object類是什么?
🟪Object 是 Java 類庫中的一個特殊類,也是所有類的父類(超類),位于類繼承層次結構的頂端。也就是說,Java 允許把任何類型的對象賦給 Object 類型的變量。🟦Java里面除了Object類,所有的類存在繼承關系的。
🟩Object 類位于 java.lang 包中,編譯時會自動導入, 當一個類被定義后,如果沒有指定繼承的父類,那么默認父類就是 Object 類。
Object類
①java.lang.Object是所有類的超類。java中所有類都實現了這個類中的方法。
②Object類是我們學習JDK類庫的第一個類。通過這個類的學習要求掌握會查閱API幫助文檔。
③現階段Object類中需要掌握的方法:
? ? ? ?● toString:將java對象轉換成字符串。
? ? ? ?● equals:判斷兩個對象是否相等。
④現階段Object類中需要了解的方法:
? ? ? ●? hashCode:返回一個對象的哈希值,通常作為在哈希表中查找該對象的鍵值。Object類的默認實現是根據對象的內存地址生成一個哈希碼(即將對象的內存地址轉換為整數作為哈希值)。??
? ? ? ●? hashCode()方法是為了HashMap、Hashtable、HashSet等集合類進行優化而設置的,以便更快地查找和存儲對象。
? ? ? ●? finalize:當java對象被回收時,由GC自動調用被回收對象的finalize方法,通常在該方法中完成銷毀前的準備。
? ? ? ●? clone:對象的拷貝。(淺拷貝,深拷貝)
? ? ? ? ? ? ? ? ? ? protected修飾的只能在同一個包下或者子類中訪問。
? ? ? ? ? ? ? ? ? ? 只有實現了Cloneable接口的對象才能被克隆。
一、toString
方法
Object類中的toString()方法:1. Object類設計toString()方法的目的是什么?這個方法的作用是:將java對象轉換成字符串的表示形式。
2. Object類中toString()方法的默認實現是怎樣的?
public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}
默認實現是:完整類名 + @ + 十六進制的數字這個輸出結果可以等同看做一個java對象的內存地址。
例如:
Date類:
public class Date {private int year;private int month;private int day;public Date() {this(1970,1,1);}public Date(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}public int getYear() {return year;}public void setYear(int year) {this.year = year;}public int getMonth() {return month;}public void setMonth(int month) {this.month = month;}public int getDay() {return day;}public void setDay(int day) {this.day = day;}
}
DateTest測試類:
public class DateTest {public static void main(String[] args) {DateTest dateTest = new DateTest();//創建DateTest對象String s = dateTest.toString();System.out.println(s);Date d = new Date();//創建Date對象String s1 = d.toString();System.out.println(s1);}
}
運行結果:
- 功能:該方法用于將 Java 對象轉換為字符串表示形式。默認情況下,
Object
類的toString
方法返回的字符串格式為類名@十六進制哈希碼
,例如com.example.MyClass@12345678
。但在實際應用中,通常會在自定義類中重寫toString
方法,以便返回更有意義的對象信息,比如對象的屬性值等。
例如:在Date類中重寫toString方法
@Overridepublic String toString() {return this.year + "年" + this.month + "月" + this.day + "日";}
運行結果:
注意點:當println()輸出的是一個引用的時候,會自動調用“引用.toString()”
Date類:
public class Date {private int year;private int month;private int day;public Date() {this(1970,1,1);}public Date(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}public int getYear() {return year;}public void setYear(int year) {this.year = year;}public int getMonth() {return month;}public void setMonth(int month) {this.month = month;}public int getDay() {return day;}public void setDay(int day) {this.day = day;}
}
DateTest測試類:
public class DateTest {public static void main(String[] args) {DateTest dateTest = new DateTest();//創建DateTest對象String s = dateTest.toString();System.out.println(s);Date d = new Date();//創建Date對象String s1 = d.toString();System.out.println(s1);Date d3 = new Date(2008, 5, 12);// 當println()輸出的是一個引用的時候,會自動調用“引用.toString()”System.out.println(d3); // 2008年5月12日System.out.println(d3.toString()); // 2008年5月12日}
}
運行結果:
Date d4 = null;System.out.println(d4); // "null"System.out.println(d4 == null ? "null" : d4.toString());//System.out.println(d4.toString()); // 空指針異常。
運行結果:
在 Java 中,當你使用?System.out.println()
?方法輸出一個引用類型的對象時,會自動調用該對象的?toString()
?方法,下面我們從源碼層面來剖析其原因。
1.?System.out
?的本質
在 Java 里,System.out
?是?PrintStream
?類的一個實例,它是標準輸出流。System
?類中有如下靜態初始化代碼塊來初始化?out
?變量:
public final class System {// ... 其他代碼 ...public final static PrintStream out = null;static {// 初始化 out 為標準輸出流setOut0(new PrintStream(new BufferedOutputStream(FileOutputStreamDescriptor.out), true));}// ... 其他代碼 ...
}
2.?println()
?方法調用流程
當你調用?System.out.println(Object x)
?方法時,會進入?PrintStream
?類中的相應?println
?方法。以下是?PrintStream
?類中?println(Object x)
?方法的源碼:
public class PrintStream extends FilterOutputStream implements Appendable, Closeable {// ... 其他代碼 ...public void println(Object x) {String s = String.valueOf(x);synchronized (this) {print(s);newLine();}}// ... 其他代碼 ...
}
在這個方法中,它首先調用了?String.valueOf(x)
?方法將傳入的對象?x
?轉換為字符串?s
,然后將這個字符串打印出來并換行。
3.?String.valueOf(Object obj)
?方法
接下來看?String.valueOf(Object obj)
?方法的源碼,它位于?String
?類中:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {// ... 其他代碼 ...public static String valueOf(Object obj) {return (obj == null) ? "null" : obj.toString();}// ... 其他代碼 ...
}
從這段源碼可以看出,String.valueOf(Object obj)
?方法會先判斷傳入的對象?obj
?是否為?null
,如果是?null
?則返回字符串?"null"
;如果不是?null
,則直接調用該對象的?toString()
?方法。
總結
綜上所述,當你使用?System.out.println()
?方法輸出一個引用類型的對象時,println
?方法內部會調用?String.valueOf()
?方法將對象轉換為字符串,而?String.valueOf()
?方法又會去調用對象的?toString()
?方法(對象不為?null
?的情況下),所以最終就實現了自動調用對象的?toString()
?方法來輸出對象的字符串表示形式。這就是為什么在 Java 中使用?println()
?輸出引用時會自動調用?引用.toString()
?的原因。
二、equals方法
?Object類中的equals方法:
1. Object類設計equals方法的作用是什么?目的是什么?
①equals方法的作用是:判斷兩個對象是否相等。
②equals方法的返回值是true/false?
③true代表兩個對象相等。
④false代表兩個對象不相等。
2. Object類中對equals方法的默認實現是怎樣的?
public boolean equals(Object obj) {return (this == obj);}
?a.equals(b) 表面是a和b的比較。實際上方法體當中是:this和obj的比較。
3. 關于 == 運算符的運算規則:
?== 永遠只有一個運算規則,永遠比較的是變量中保存的值之間的比較。? ? ?
只不過有的時候這個值是基本數據類型。有的時候這個值是對象的內存地址。
⑴基本數據類型:
當==
用于比較兩個基本數據類型(如int
、byte
、char
、long
、float
、double
、boolean
)時,它直接比較的是它們的值。例如:
int num1 = 5;
int num2 = 5;
System.out.println(num1 == num2); // 輸出 true
⑵引用數據類型:
對于引用數據類型(如類、接口、數組等),==
比較的是兩個變量所保存的對象的內存地址。也就是說,如果兩個引用變量指向同一個對象,那么==
比較的結果為true
;否則為false
。例如:
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // 輸出 false,因為str1和str2指向不同的對象
⑶而在 Java 中,要比較兩個對象的內容是否相等,通常需要使用對象的equals
方法(前提是該類已經重寫了equals
方法)。例如,對于String
類,equals
方法被重寫來比較字符串的內容:
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2)); // 輸出 true
總結來說,==
運算符在基本數據類型和引用數據類型的比較行為上有所不同,使用時需要注意。在比較對象內容時,通常使用equals
方法更為合適。
4. equals方法為什么要重寫??
因為Object類中的equals方法在進行比較的時候,比較的是兩個java對象的內存地址。
我們希望比較的是對象的內容。只要對象的內容相等,則認為是相同的。
5.注意點
字符串的比較不能使用 ==,必須使用equals方法進行比較。
⑴字符串String類型已經重寫了equals方法。
在Java中,String
類確實重寫了Object
類的equals
方法,用于比較兩個字符串對象的內容是否相等,而不是比較對象的內存地址(即==
的比較邏輯)。這是字符串操作中非常關鍵的特性,也是String
類設計的核心之一。
String類的equals方法源碼解析
String
類中的equals
方法重寫如下:
public boolean equals(Object anObject) {if (this == anObject) { // 1. 先檢查是否是同一個對象(內存地址相同)return true;}if (anObject instanceof String) { // 2. 檢查是否為String類型String anotherString = (String) anObject;int n = value.length;if (n == anotherString.value.length) { // 3. 比較長度是否一致char v1[] = value; // 當前字符串的字符數組char v2[] = anotherString.value; // 目標字符串的字符數組int i = 0;while (n-- != 0) { // 4. 逐個字符比較if (v1[i] != v2[i])return false;i++;}return true;}}return false;
}
關鍵特性
-
內容比較
equals
方法比較的是字符串的實際字符內容,而非對象的內存地址。例如:String s1 = new String("hello"); String s2 = new String("hello"); System.out.println(s1.equals(s2)); // true(內容相同) System.out.println(s1 == s2); // false(內存地址不同)
-
效率優化
-
先檢查是否是同一個對象(
this == anObject
),直接返回true
。 -
比較長度是否相同,長度不同直接返回
false
。
-
-
字符級比較
對字符串的每個字符進行逐一比對,確保所有字符完全一致。
⑵equals方法重寫需要“徹底”重寫。
Address類:
public class Address {private String city;private String street;public Address() {}public Address(String city, String street) {this.city = city;this.street = street;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +'}';}@Overridepublic boolean equals(Object obj) {if(obj == null) return false;if(this == obj) return true;if(obj instanceof Address){Address a = (Address) obj;return this.city.equals(a.city) && this.street.equals(a.street);}return false;}
}
User類:
public class User {private String name;private Address address;//對象引用public User() {}public User(String name, Address address) {this.name = name;this.address = address;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", address=" + address +'}';}@Overridepublic boolean equals(Object obj) {// u.equals(u2)// this就是u obj就是u2if(obj == null) return false;if(this == obj) return true;if(obj instanceof User){User user = (User) obj;if(this.name.equals(user.name) && this.address.equals(user.address)){return true;}}return false;}
}
Test類:
public class Test {public static void main(String[] args) {// 創建家庭住址對象//得先創建家庭住址對象,才能創建用戶對象Address a = new Address("北京", "大興");// 創建用戶對象User u = new User("張三", a);// 創建家庭住址對象2Address a2 = new Address("北京", "大興");// 創建用戶對象2User u2 = new User("張三", a2);//輸出false,雖然u和u2內容一樣,但它們的內存地址不一樣System.out.println(u.equals(u2));}
}
運行結果:
“徹底” 重寫的理解
? ? ? “徹底” 重寫?
equals
?方法要求在比較對象時,不僅要比較當前類的屬性,還要遞歸地比較所有引用類型的屬性。在?User
?類中,address
?是一個引用類型的屬性,如果?Address
?類沒有重寫?equals
?方法,那么在?User
?類的?equals
?方法中比較?address
?屬性時,默認會使用?Object
?類的?equals
?方法,即比較引用是否相等,而不是比較?address
?對象的內容是否相等。這樣即使兩個?User
?對象的?name
?和?address
?的內容都相同,但由于?address
?對象的引用不同,equals
?方法仍然會返回?false
,這顯然不符合我們的預期。? ? ? ?因此,為了確保?
equals
?方法能夠準確地比較兩個對象的內容是否相等,我們需要在每個包含引用類型屬性的類中重寫?equals
?方法,并且在比較引用類型屬性時,調用該屬性所屬類重寫的?equals
?方法,從而實現 “徹底” 重寫。
三、hashCode()方法
關于Object類的hashCode()方法: * hashCode:返回一個對象的哈希值,通常作為在哈希表中查找該對象的鍵值。 * Object類的默認實現是根據對象的內存地址生成一個哈希碼(即將對象的內存地址轉換為整數作為哈希值)。 * hashCode()方法是為了HashMap、Hashtable、HashSet等集合類進行優化而設置的,以便更快地查找和存儲對象
* * hashCode()方法在Object類中的默認實現: 這是一個本地方法,底層調用了C++寫的動態鏈接庫程序:xxx.dll
示例1:
public class Test01 {public static void main(String[] args) {Test01 t = new Test01();int i = t.hashCode();System.out.println(i); //隨機輸出哈希碼Test01 t2 = new Test01();int i1 = t2.hashCode();System.out.println(i1);System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());}
}
運行結果:
- 功能描述:返回該對象的哈希碼值。哈希碼主要用于在哈希表等數據結構中快速定位和存儲對象。在
Object
類中,默認的哈希碼是根據對象的內存地址生成的。但如果重寫了equals
方法,通常也需要重寫hashCode
方法,以保證相等的對象具有相同的哈希碼,這是為了滿足一些集合類(如HashMap
、HashSet
)的內部邏輯要求。
四、finalize()
方法
關于Object類中的finalize()方法: * finalize:當java對象被回收時,由GC自動調用被回收對象的finalize方法,通常在該方法中完成銷毀前的準備 * 從Java9開始,這個方法被標記已過時,不建議使用。作為了解。
* 在Object類中是這樣實現的:很顯然,這個方法是需要子類重寫的。
protected void finalize() throws Throwable { }
- 功能描述:當垃圾回收器確定不存在對該對象的更多引用時,由垃圾回收器在回收對象之前調用此方法。在這個方法中,可以進行一些資源釋放、清理等操作,但不建議過度依賴這個方法,因為垃圾回收的時機是不確定的,而且在 Java 9 及以后的版本中,
finalize
方法已被標記為@Deprecated
,逐漸不推薦使用。
回收對象的例子:
Person類:
public class Person {@Overrideprotected void finalize() throws Throwable {System.out.println(this + "即將被回收");}
}
測試類Test02:
public class Test02 {public static void main(String[] args) {for (int i = 0; i < 10000; i++) {//垃圾到一定程度才會調用垃圾回收器Person p1 = new Person();p1 = null; //p1等于null,即為垃圾對象// 建議啟動垃圾回收器(這只是建議啟動垃圾回收器)if(i % 1000 == 0){System.gc();//System里的gc()方法就是啟動垃圾回收器}}}
}
運行結果:
五、clone()
方法
1.淺拷貝(Shallow Copy)
定義
淺拷貝會創建一個新對象,新對象的基本數據類型屬性會復制一份新的值,而對于引用數據類型的屬性,僅僅復制其引用,也就是新對象和原對象的引用類型屬性會指向同一個內存地址。這意味著如果通過新對象修改引用類型屬性所指向的對象內容,原對象的該屬性內容也會被修改。
實現條件
在 Java 中,要實現淺拷貝,類需要滿足以下條件:
- 實現?
Cloneable
?接口。- 重寫?
Object
?類的?clone()
?方法。
User類:
public class User {private int age;public User() {}public User(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"age=" + age +'}';}}
諾添加以下代碼
當你在 Java 代碼中遇到?Unhandled exception: java.lang.CloneNotSupportedException
?錯誤時,這通常是因為你調用了?clone()
?方法,但沒有對可能拋出的?CloneNotSupportedException
?異常進行處理。下面為你詳細分析該異常出現的原因以及解決辦法。
異常原因
在 Java 里,clone()
?方法定義在?Object
?類中,其簽名為?protected native Object clone() throws CloneNotSupportedException
。這表明?clone()
?方法可能會拋出?CloneNotSupportedException
?異常。如果一個類沒有實現?Cloneable
?接口卻調用了?clone()
?方法,或者調用?clone()
?方法時沒有對?CloneNotSupportedException
?進行處理,就會產生這個編譯錯誤。
解決辦法
1. 實現?Cloneable
?接口
要使用?clone()
?方法,類必須實現?Cloneable
?接口。Cloneable
?接口是一個標記接口,本身不包含任何方法,它只是告訴 Java 虛擬機該類可以被克隆。
2. 處理?CloneNotSupportedException
?異常
可以使用?try-catch
?塊捕獲該異常,或者在方法簽名中使用?throws
?關鍵字聲明拋出該異常。
public void Test() throws CloneNotSupportedException {this.clone();}
UserTest類
public class UserTest {public static void main(String[] args) {//創建User對象User user=new User(20);//克隆一個user對象user.clone();}
}
// 克隆一個user對象 // 報錯原因:因為Object類中的clone()方法是protected修飾的。 // protected修飾的只能在:本類,同包,子類中訪問。
Object
?類中的?clone()
?方法定義如下:
protected native Object clone() throws CloneNotSupportedException;
怎么解決clone()方法的調用問題?
在子類中重寫該clone()方法。
? ?為了保證clone()方法在任何位置都可以調用,建議將其修飾符修改為:public
代碼如下:
User類:
public class User {private int age;public User() {}public User(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"age=" + age +'}';}/* public void Test() throws CloneNotSupportedException {this.clone();}*/@Overridepublic Object clone() throws CloneNotSupportedException{return super.clone();}
}
UserTest類:
public class UserTest {public static void main(String[] args) throws CloneNotSupportedException {//創建User對象User user=new User(20);//克隆一個user對象Object obj=user.clone();//克隆出的新對象,用Object去接收}
}
運行結果:
凡事參加克隆的對象,必須實現一個標志接口:java.lang.Cloneable * ? ? ?
java中接口包括兩大類:? 一類是:起到標志的作用,標志型接口。?
?另一類是:普通接口。
當你遇到?java.lang.CloneNotSupportedException
?異常時,這表明你嘗試克隆一個不支持克隆操作的對象。在 Java 中,要使用?clone()
?方法進行對象克隆,被克隆的類必須滿足以下兩個條件:
- 該類必須實現?
java.lang.Cloneable
?接口,Cloneable
?是一個標記接口,它本身不包含任何方法,只是用于告訴 Java 虛擬機該類可以被克隆。 - 通常需要在該類中重寫?
Object
?類的?clone()
?方法。
異常原因分析
根據你給出的異常堆棧信息?Exception in thread "main" java.lang.CloneNotSupportedException: lianxi.oop29.User
,可以知道問題出在?lianxi.oop29.User
?類上,該類可能沒有實現?Cloneable
?接口,從而導致調用?clone()
?方法時拋出?CloneNotSupportedException
?異常。
User類:
public class User implements Cloneable{//打個標記,該類被克隆了private int age;public User() {}public User(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"age=" + age +'}';}/* public void Test() throws CloneNotSupportedException {this.clone();}*/@Overridepublic Object clone() throws CloneNotSupportedException{return super.clone();}
}
UserTest類:
public class UserTest {public static void main(String[] args) throws CloneNotSupportedException {//創建User對象User user=new User(20);//克隆一個user對象Object obj=user.clone();//克隆出的新對象,用Object去接收System.out.println(user);// 修改克隆之后的對象的age屬性User copyUser = (User) obj;copyUser.setAge(100);System.out.println("克隆之后的新對象的年齡:" + copyUser.getAge());System.out.println("原始對象的年齡:" + user.getAge());}
}
運行結果:
-
淺拷貝的定義:對于基本數據類型字段直接復制值,對于引用類型字段僅復制引用地址(共享同一對象)。
-
代碼實現:
-
User
?類實現了?Cloneable
?接口,并調用?super.clone()
。 -
由于?
age
?是?int
(基本數據類型),拷貝時會直接復制值,因此修改拷貝對象的?age
?不會影響原始對象。 -
若?
User
?類中存在引用類型字段(例如?Address
?對象),淺拷貝會導致原始對象和拷貝對象共享該引用對象(修改一處會影響另一處)。
-
?為什么輸出結果中?age
?不同?
-
基本數據類型的特點:
int
?的值直接存儲在對象內存中,淺拷貝會直接復制該值到新對象。 -
代碼邏輯驗證:
User user = new User(20); // 原始對象 age=20 User copyUser = (User) user.clone(); // 拷貝對象 age=20(獨立值) copyUser.setAge(100); // 僅修改拷貝對象的 age
-
最終?
user.age
?仍為?20
,copyUser.age
?變為?100
。
-
?如果?User
?類包含引用類型字段,淺拷貝會如何?
假設?User
?類中添加引用類型字段?Address
:
class User implements Cloneable {private int age;private Address address; // 引用類型字段// 省略其他代碼...@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone(); // 淺拷貝:address字段共享同一對象}
}class Address {private String city;// 省略 getter/setter...
}
-
驗證代碼:
User user = new User(20, new Address("Beijing")); User copyUser = (User) user.clone();copyUser.getAddress().setCity("Shanghai"); System.out.println(user.getAddress().getCity()); // 輸出 "Shanghai"(被修改)
-
淺拷貝的副作用:原始對象和拷貝對象共享?
address
,修改一處會影響另一處。
-
淺拷貝內存圖
Address類:
public class Address {private String city;private String street;public Address() {}public Address(String city, String street) {this.city = city;this.street = street;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +'}';}
}
User類:
public class User implements Cloneable{private String name;private Address addr;public User() {}public User(String name, Address addr) {this.name = name;this.addr = addr;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", addr=" + addr +'}';}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}
測試類Test:
public class Test {public static void main(String[] args) throws CloneNotSupportedException {//創建住址對象Address a=new Address("北京","海淀");//創建User對象User user1=new User("李四",a);User user2=(User) user1.clone();//克隆一個User對象因為返回的是Object對象,所以需要轉型System.out.println(user1);System.out.println(user2);user2.getAddr().setCity("天津");System.out.println("===================");System.out.println(user1);System.out.println(user2);}
}
運行結果:
jvm圖
解釋:
? ? ?克隆時,只克隆了User類型對象,Address沒有一起克隆
? ? ?當修改city為“天津”時,city=ox11指向“天津”,因為User1和user2同時指向Address,所以同時修改為“天津”
? ? ? 當引用數據類型需要克隆時(即Addr),則為深克隆
2.深拷貝(Deep Copy)
定義
深拷貝會創建一個新對象,新對象的所有屬性,包括基本數據類型和引用數據類型,都會被復制一份新的值。這意味著新對象和原對象在內存中是完全獨立的,修改新對象的任何屬性都不會影響原對象。
實現方式
實現深拷貝有多種方式,常見的有手動實現和使用序列化與反序列化。
手動實現深拷貝
手動實現深拷貝需要在?clone()
?方法中遞歸地復制引用類型的屬性。
實現代碼:
Address類:
public class Address {private String city;private String street;public Address() {}public Address(String city, String street) {this.city = city;this.street = street;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +'}';}
}
User類:
public class User implements Cloneable{private String name;private Address addr;public User() {}public User(String name, Address addr) {this.name = name;this.addr = addr;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", addr=" + addr +'}';}
/*@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}*/@Overridepublic Object clone() throws CloneNotSupportedException {// 重寫方法,讓其達到深克隆的效果。// User要克隆,User對象關聯的Address對象也需要克隆一份。Address copyAddr = (Address)this.getAddr().clone();//克隆一份AddrUser copyUser = (User)super.clone();//克隆一份UsercopyUser.setAddr(copyAddr); //把之前克隆的Addr的地址給新Userreturn copyUser;}
}
但是注意:克隆Address,Address也要重寫clone()方法,不然會報錯
步驟:1.Address類添加克隆標識
? ? ? ? ? ?2.重寫clone()方法
? ? ? ? ? ?3.protected修改為public
所以完整代碼為:
Address類:
public class Address implements Cloneable{private String city;private String street;public Address() {}public Address(String city, String street) {this.city = city;this.street = street;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +'}';}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}
User類:
public class User implements Cloneable{private String name;private Address addr;public User() {}public User(String name, Address addr) {this.name = name;this.addr = addr;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", addr=" + addr +'}';}
/*@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}*/@Overridepublic Object clone() throws CloneNotSupportedException {// 重寫方法,讓其達到深克隆的效果。// User要克隆,User對象關聯的Address對象也需要克隆一份。Address copyAddr = (Address)this.getAddr().clone();//克隆一份AddrUser copyUser = (User)super.clone();//克隆一份UsercopyUser.setAddr(copyAddr); //把之前克隆的Addr的地址給新Userreturn copyUser;}
}
測試類Test:
public class Test {public static void main(String[] args) throws CloneNotSupportedException {//創建住址對象Address a=new Address("北京","海淀");//創建User對象User user1=new User("李四",a);User user2=(User) user1.clone();//克隆一個User對象因為返回的是Object對象,所以需要轉型System.out.println(user1);System.out.println(user2);user2.getAddr().setCity("天津");System.out.println("===================");System.out.println(user1);System.out.println(user2);}
}
運行結果為: