java對象克隆實現(xiàn)方法詳解
概述:
當(dāng)我們new一個對象時,其中的屬性就會被初始化, 那么想要保存剛開始初始化的值就靠clone方法來實現(xiàn), 平時我們最常見的是一個對象的引用指向另一個對象,并不是創(chuàng)建了兩個對象.
Person p1 = new Person(100,"jim");
Person p2 = p1;
System.out.println(p1==p2);//true克隆肯定是創(chuàng)建了兩個對象
Person p1 = new Person(100,"jim");
Person p2 =p1.clone();//克隆的新對象
System.out.println(p1==p2);//false克隆分為淺克隆(ShallowClone)和深克隆(DeepClone)。
在 Java 語言中,數(shù)據(jù)類型分為值類型(基本數(shù)據(jù)類型)和引用類型,值類型包括 int、double、byte、boolean、char 等簡單數(shù)據(jù)類型,引用類型包括類、接口、數(shù)組等復(fù)雜類型?;绢愋偷闹悼梢灾苯訌?fù)制,引用類型只能復(fù)制引用地址。所以淺克隆和深克隆的主要區(qū)別在于是否支持引用類型的成員變量的復(fù)
制.
淺克隆
在淺克隆中,如果原型對象的成員變量是值類型,將復(fù)制一份給克隆對象;如果原型對象的成員變量是引用類型,則將引用對象的地址復(fù)制一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的內(nèi)存地址。簡單來說,在淺克隆中,當(dāng)對象被復(fù)制時只復(fù)制它本身和其中包含的值類型的成員變量,而引用類型的成員對象并沒有復(fù)制。
實現(xiàn)方式:
1.在 Java 語言中,通過覆蓋 Object 類的 clone()方法可以實現(xiàn)淺克隆。
2.在 spring 框架中提供 BeanUtils.copyProperties(source,target);
這里我們主要演示通過重寫object中clone方法來實現(xiàn)
1.首先定義一個類(需要被克隆的類)
public class Person implements Cloneable{
int num;
String name;
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
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;
}
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}2.可以看到Person類關(guān)聯(lián)著Address類,也寫出來
public class Address {
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}3.寫一個Test類進(jìn)行測試
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setAddress("漢中");
Person p1 = new Person(100,"jim");
p1.setAddress(address);
Person p2 =p1.clone();
p2.setName("tom");
address.setAddress("西安");
System.out.println(p1); // jim 西安
System.out.println(p2);// tom 西安
}
}首先看name屬性,p1的name為jim,克隆出另一個對象p2,將name改成了tom,因為是兩個對象,所以輸出的結(jié)果分別都不同.
再看關(guān)聯(lián)對象, 首先將有漢中信息的address加入到了p1中,所以目前p1中的address是漢中,經(jīng)過克隆出p2后,其實對于address來說只克隆了地址,所以說p1和p2指向的都是同一個address,所以都是漢中,再經(jīng)過一次修改成了西安,所以都是西安.
所以說淺克隆只是克隆了引用類型變量的地址.
深克隆
在深克隆中,無論原型對象的成員變量是值類型還是引用類型,都將復(fù)制一份給克隆對象,深克隆將原型對象的所有引用對象也復(fù)制一份給克隆對象。簡單來說,在深克隆中,除了對象本身被復(fù)制外,對象所包含的所有成員變量也將復(fù)制
在 Java 語言中,如果需要實現(xiàn)深克隆,可以通過覆蓋 Object 類的 clone()方法實現(xiàn),也可以通過序列化(Serialization)等方式來實現(xiàn)。序列化就是將對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在于內(nèi)存中。通過序列化實現(xiàn)的拷貝不僅可以復(fù)制對象本身,而且可以復(fù)制其引用的成員對象,因此通過序列化將對象寫到一個流中,再從流里將其讀出來,可以實現(xiàn)深克隆。需要注意的是能夠?qū)崿F(xiàn)序列化的對象其類必須實現(xiàn)Serializable 接口,否則無法實現(xiàn)序列化操作
1.重寫Object類中的clone方法
1.首先定義一個類(需要被克隆的類),相比于上面的淺克隆增加了一行克隆address對象的代碼
public class Person implements Cloneable{
int num;
String name;
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
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;
}
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();
person.address = (Address)address.clone(); //深度復(fù)制 聯(lián)同person中關(guān)聯(lián)的對象也一同克隆.
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}2.Address寫出來,相比于上面淺克隆多了一個重寫Object類的clone方法
public class Address {
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
}3.還是Test測試類
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setAddress("漢中");
Person p1 = new Person(100,"jim");
p1.setAddress(address);
Person p2 =p1.clone();
p2.setName("tom");
address.setAddress("西安");
System.out.println(p1); // jim 西安
System.out.println(p2);// tom 漢中
}
}這次的結(jié)果會有所不同, 因為深克隆不僅克隆了自己, 還克隆了關(guān)聯(lián)著的類的對象, 所以說原來的p1存儲的漢中被克隆在了p2中,而最后一行更改為西安的是原來的address對象, p2中已經(jīng)克隆了原來的address并且保存了下來
2.序列化(Serialization)的方式
如果需要被克隆的類中關(guān)聯(lián)的其他類的對象太多, 那么繼續(xù)用深克隆的話需要耗費(fèi)大量的時間去一個一個克隆關(guān)聯(lián)著的對象, 而序列化的方式可以將該類中所有關(guān)聯(lián)的對象化成流從而高校的進(jìn)行克隆.
1.還是創(chuàng)建一個關(guān)聯(lián)著被克隆的類的對象
public class Address implements Serializable {
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}2.Person類,里面寫一個自己的序列化方式的克隆方法
public class Person implements Serializable {
int num;
String name;
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
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;
}
/**
* 自定義克隆方法
* @return
*/
public Person myclone() {
Person person = null;
try { // 將該對象序列化成流,因為寫在流里的是對象的一個拷貝,而原對象仍然存在于JVM里面。所以利用這個特性可以實現(xiàn)對象的深拷貝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 將流序列化成對象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
person = (Person) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}3.Test類測試
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setAddress("漢中");
Person p1 = new Person(100,"jim");
p1.setAddress(address);
Person p2 =p1.myclone();
p2.setName("tom");
address.setAddress("西安");
System.out.println(p1);jim 西安
System.out.println(p2);tom 漢中
}
}所以說, 得看具體情況進(jìn)行選擇
總結(jié)
到此這篇關(guān)于java對象克隆實現(xiàn)的文章就介紹到這了,更多相關(guān)java對象克隆內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成shiro,MyRealm中無法@Autowired注入Service的問題
今天小編就為大家分享一篇關(guān)于SpringBoot集成shiro,MyRealm中無法@Autowired注入Service的問題,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03
SpringBoot配置Redis實現(xiàn)保存獲取和刪除數(shù)據(jù)
本文主要介紹了SpringBoot配置Redis實現(xiàn)保存獲取和刪除數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,感興趣的小伙伴們可以參考一下2021-06-06
MyBatis實現(xiàn)Mysql數(shù)據(jù)庫分庫分表操作和總結(jié)(推薦)
這篇文章主要介紹了MyBatis實現(xiàn)Mysql數(shù)據(jù)庫分庫分表操作和總結(jié),需要的朋友可以參考下2017-08-08
自主配置數(shù)據(jù)源,mybatis/plus不打印sql日志問題
這篇文章主要介紹了自主配置數(shù)據(jù)源,mybatis/plus不打印sql日志問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
Java中高效判斷數(shù)組中是否包含某個元素的幾種方法
這是一個在Java中經(jīng)常用到的并且非常有用的操作,這個問題在Stack Overflow中也是一個非常熱門的問題,本文將分析幾種常見用法及其時間成本2021-09-09
java數(shù)據(jù)庫批量插入數(shù)據(jù)的實現(xiàn)
本文主要介紹了java數(shù)據(jù)庫批量插入數(shù)據(jù)的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05
mybatis-plus的多租戶不同版本實現(xiàn)的兩種方式
本文主要介紹了mybatis-plus的多租戶不同版本實現(xiàn)的兩種方式,Mybatis Plus 3.4.0版本之后多租戶的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2025-03-03

