Java中的object類

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. 關于 == 運算符的運算規則:

?== 永遠只有一個運算規則,永遠比較的是變量中保存的值之間的比較。? ? ?

只不過有的時候這個值是基本數據類型。有的時候這個值是對象的內存地址。

⑴基本數據類型

==用于比較兩個基本數據類型(如intbytecharlongfloatdoubleboolean)時,它直接比較的是它們的值。例如:

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;
}

關鍵特性

  1. 內容比較
    equals方法比較的是字符串的實際字符內容,而非對象的內存地址。例如:

    String s1 = new String("hello");
    String s2 = new String("hello");
    System.out.println(s1.equals(s2)); // true(內容相同)
    System.out.println(s1 == s2);      // false(內存地址不同)
  2. 效率優化

    • 先檢查是否是同一個對象(this == anObject),直接返回true

    • 比較長度是否相同,長度不同直接返回false

  3. 字符級比較
    對字符串的每個字符進行逐一比對,確保所有字符完全一致。

⑵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方法,以保證相等的對象具有相同的哈希碼,這是為了滿足一些集合類(如HashMapHashSet)的內部邏輯要求。

四、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()?方法進行對象克隆,被克隆的類必須滿足以下兩個條件:

  1. 該類必須實現?java.lang.Cloneable?接口,Cloneable?是一個標記接口,它本身不包含任何方法,只是用于告訴 Java 虛擬機該類可以被克隆。
  2. 通常需要在該類中重寫?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?仍為?20copyUser.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);}
}

運行結果為:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/67990.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67990.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67990.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

uniapp小程序自定義中間凸起樣式底部tabbar

我自己寫的自定義的tabbar效果圖 廢話少說咱們直接上代碼&#xff0c;一步一步來 第一步&#xff1a; 找到根目錄下的 pages.json 文件&#xff0c;在 tabBar 中把 custom 設置為 true&#xff0c;默認值是 false。list 中設置自定義的相關信息&#xff0c; pagePath&#x…

四、GPIO中斷實現按鍵功能

4.1 GPIO簡介 輸入輸出&#xff08;I/O&#xff09;是一個非常重要的概念。I/O泛指所有類型的輸入輸出端口&#xff0c;包括單向的端口如邏輯門電路的輸入輸出管腳和雙向的GPIO端口。而GPIO&#xff08;General-Purpose Input/Output&#xff09;則是一個常見的術語&#xff0c…

vscode+CMake+Debug實現 及權限不足等諸多問題匯總

環境說明 有空再補充 直接貼兩個json tasks.json {"version": "2.0.0","tasks": [{"label": "cmake","type": "shell","command": "cmake","args": ["../"…

【Elasticsearch】post_filter

post_filter是 Elasticsearch 中的一種后置過濾機制&#xff0c;用于在查詢執行完成后對結果進行過濾。以下是關于post_filter的詳細介紹&#xff1a; 工作原理 ? 查詢后過濾&#xff1a;post_filter在查詢執行完畢后對返回的文檔集進行過濾。這意味著所有與查詢匹配的文檔都…

《數據可視化新高度:Graphy的AI協作變革》

在數據洪流奔涌的時代&#xff0c;企業面臨的挑戰不再僅僅是數據的收集&#xff0c;更在于如何高效地將數據轉化為洞察&#xff0c;助力決策。Graphy作為一款前沿的數據可視化工具&#xff0c;憑借AI賦能的團隊協作功能&#xff0c;為企業打開了數據協作新局面&#xff0c;重新…

Vue 2 與 Vue 3 的主要區別

Vue.js 是一個流行的前端框架&#xff0c;用于構建用戶界面和單頁應用。自從 Vue 2 發布以來&#xff0c;社區對其進行了廣泛的應用和擴展&#xff0c;而 Vue 3 的發布則帶來了許多重要的改進和新特性。 性能提升 Vue 3 在響應式系統上進行了重大的改進&#xff0c;采用了基于…

從零開始:用Qt開發一個功能強大的文本編輯器——WPS項目全解析

文章目錄 引言項目功能介紹1. **文件操作**2. **文本編輯功能**3. **撤銷與重做**4. **剪切、復制與粘貼**5. **文本查找與替換**6. **打印功能**7. **打印預覽**8. **設置字體顏色**9. **設置字號**10. **設置字體**11. **左對齊**12. **右對齊**13. **居中對齊**14. **兩側對…

【IoCDI】_Spring的基本掃描機制

目錄 1. 創建測試項目 2. 改變啟動類所屬包 3. 使用ComponentScan 4. Spring基本掃描機制 程序通過注解告訴Spring希望哪些bean被管理&#xff0c;但在僅使用Bean時已經發現&#xff0c;Spring需要根據五大類注解才能進一步掃描方法注解。 由此可見&#xff0c;Spring對注…

vue 引入百度地圖和高德天氣 都得獲取權限

vue接入百度地圖---獲取ak https://blog.csdn.net/qq_57144407/article/details/143430661 vue接入高德天氣&#xff0c; 需要授權----獲取key https://www.jianshu.com/p/09ddd698eebe

通向AGI之路:人工通用智能的技術演進與人類未來

文章目錄 引言:當機器開始思考一、AGI的本質定義與技術演進1.1 從專用到通用:智能形態的范式轉移1.2 AGI發展路線圖二、突破AGI的五大技術路徑2.1 神經符號整合(Neuro-Symbolic AI)2.2 世界模型架構(World Models)2.3 具身認知理論(Embodied Cognition)三、AGI安全:價…

python中的命名規范

在python中&#xff0c;命名規范是編寫清晰&#xff0c;可讀性強代碼的重要部分&#xff0c;遵循這些規范可以使代碼更易于理解和維護。 Type命名約定命名例子函數&#xff08;Function&#xff09;小寫單詞&#xff0c;下劃線分割單詞function,delta_function方法&#xff08…

【工具變量】中國省級八批自由貿易試驗區設立及自貿區設立數據(2024-2009年)

一、測算方式&#xff1a;參考C刊《中國軟科學》任曉怡老師&#xff08;2022&#xff09;的做法&#xff0c;使用自由貿易試驗區(Treat Post) 表征&#xff0c;Treat為個體不隨時間變化的虛擬變量&#xff0c;如果該城市設立自由貿易試驗區則賦值為1&#xff0c;反之賦值為0&am…

Java進階總結——集合

Java進階總結——集合 說明&#xff1a;對于以上的框架圖有如下幾點說明 1.所有集合類都位于java.util包下。Java的集合類主要由兩個接口派生而出&#xff1a;Collection和Map&#xff0c;Collection和Map是Java集合框架的根接口&#xff0c;這兩個接口又包含了一些子接口或實…

計算機視覺和圖像處理

計算機視覺與圖像處理的最新進展 隨著人工智能技術的飛速發展&#xff0c;計算機視覺和圖像處理作為其中的重要分支&#xff0c;正逐步成為推動科技進步和產業升級的關鍵力量。 一、計算機視覺的最新進展 計算機視覺&#xff0c;作為人工智能的重要分支&#xff0c;主要研究如…

3.PPT:華老師-計算機基礎課程【3】

目錄 NO12? NO34? NO56? NO789? NO12 根據考生文件夾下的Word文檔“PPT素材.docx”中提供的內容在PPT.pptx中生成初始的6張幻燈片 新建幻燈片6張→ctrlc復制→ctrlv粘貼開始→新建幻燈片→幻燈片(從大綱)→Word文檔注?前提是&#xff1a;Word文檔必須應用標題1、標題2…

(三)QT——信號與槽機制——計數器程序

目錄 前言 信號&#xff08;Signal&#xff09;與槽&#xff08;Slot&#xff09;的定義 一、系統自帶的信號和槽 二、自定義信號和槽 三、信號和槽的擴展 四、Lambda 表達式 總結 前言 信號與槽機制是 Qt 中的一種重要的通信機制&#xff0c;用于不同對象之間的事件響…

藍橋杯備賽題目練習(一)

一. 口算練習題 ## 題目描述 王老師正在教簡單算術運算。細心的王老師收集了 i 道學生經常做錯的口算題&#xff0c;并且想整理編寫成一份練習。 編排這些題目是一件繁瑣的事情&#xff0c;為此他想用計算機程序來提高工作效率。王老師希望盡量減少輸入的工作量&#xff0c;比…

深入探討:服務器如何響應前端請求及后端如何查看前端提交的數據

深入探討&#xff1a;服務器如何響應前端請求及后端如何查看前端提交的數據 一、服務器如何響應前端請求 前端與后端的交互主要通過 HTTP 協議實現。以下是詳細步驟&#xff1a; 1. 前端發起 HTTP 請求 GET 請求&#xff1a;用于從服務器獲取數據。POST 請求&#xff1a;用…

毫秒級響應的VoIP中的系統組合推薦

在高并發、低延遲、毫秒級響應的 VoIP 場景中&#xff0c;選擇合適的操作系統組合至關重要。以下是針對 Ubuntu linux-lowlatency、CentOS Stream kernel-rt 和 Debian 自定義 PREEMPT_RT 的詳細對比及推薦&#xff1a; 1. 系統組合對比 特性Ubuntu linux-lowlatencyCentO…

【LeetCode 刷題】回溯算法(4)-排列問題

此博客為《代碼隨想錄》二叉樹章節的學習筆記&#xff0c;主要內容為回溯算法排列問題相關的題目解析。 文章目錄 46.全排列47.全排列 II 46.全排列 題目鏈接 class Solution:def permute(self, nums: List[int]) -> List[List[int]]:res, path [], []used [0] * len(n…