1烂斋、什么是深拷貝屹逛、什么是淺拷貝
淺拷貝:淺拷貝不會(huì)生成新的對(duì)象,只會(huì)在原對(duì)象上增加了一個(gè)新的對(duì)象引用汛骂,兩個(gè)引用指向的對(duì)象是同一個(gè)罕模;
深拷貝:深拷貝是重新生成一個(gè)新的對(duì)象,然后把原來(lái)對(duì)象的值拷貝到新對(duì)象的一個(gè)過(guò)程帘瞭,新的對(duì)象和原來(lái)的對(duì)象是沒(méi)有任何關(guān)系的手销。
2、淺拷貝場(chǎng)景:
java 對(duì)象默認(rèn)的對(duì)象拷貝方式都為淺拷貝图张,下面我們來(lái)看一下我們程序里面常見(jiàn)的例子:
(1)首先定義一個(gè)User類(lèi)
public classUser {private int age;//年齡privateStringname;//姓名public intgetAge() {return age;
}public voidsetAge(intage) {this.age= age;
}publicString getName() {return name;
}public voidsetName(String name) {this.name= name;
}
@OverridepublicString toString() {return "User{"+"age="+age+", name='"+name+'\''+'}';
}
}
(2)測(cè)試代碼:
public static voidmain(String[] args) {//1锋拖、實(shí)例化一個(gè)user1對(duì)象,并對(duì)屬性賦值User user1=newUser();
user1.setName("我是user1");
user1.setAge(18);//2祸轮、把user1對(duì)象放到一個(gè)List里面List userList=newArrayList();
userList.add(user1);//3兽埃、然后創(chuàng)建user2 對(duì)象并從List里面拿出user1對(duì)象賦值給user2User user2= userList.get(0);//4、給user2的屬性值user2.setAge(1);
user2.setName("我是user2");//5适袜、這個(gè)的時(shí)候我們?cè)佥敵鰑ser1的對(duì)應(yīng)屬性值System.out.println(user1);
}
最后我們看看打印結(jié)果:
舉了一個(gè)比較繞的例子是想更好的提現(xiàn)出淺拷貝可能會(huì)給我們帶來(lái)的問(wèn)題柄错;
結(jié)合淺拷貝的圖解我想我們不難理解這個(gè)結(jié)果是怎么出現(xiàn)的,因?yàn)閡ser1和user2其實(shí)指向的是同一個(gè)對(duì)象,所以當(dāng)我們修改user2的屬性時(shí)其實(shí)修改的也是user1這個(gè)對(duì)象苦酱。
程序中如果像這種在一個(gè)對(duì)象上多次淺拷貝并使用其實(shí)是很危險(xiǎn)的售貌,有時(shí)候調(diào)用的層次多了被傳遞的使用者修改了對(duì)象屬性會(huì)造成業(yè)務(wù)邏輯上的錯(cuò)誤(想想上面的例子,如果在user2修改屬性值之后疫萤,還有業(yè)務(wù)代碼要拿user1來(lái)進(jìn)行業(yè)務(wù)操作的話(huà)颂跨,那么此時(shí)的user1屬性值都已經(jīng)被修改了,這樣勢(shì)必會(huì)產(chǎn)生業(yè)務(wù)上的錯(cuò)誤)扯饶,而這樣的問(wèn)題又比較難發(fā)現(xiàn)恒削,并且這樣也會(huì)造成代碼的理解成本變高,可讀性也會(huì)變差尾序。
總結(jié):因?yàn)闇\拷貝的特性我們要盡少對(duì)淺拷貝的對(duì)象進(jìn)行屬性值的修改钓丰,如果有需要拷貝對(duì)象的時(shí)候,我們可以考慮用對(duì)象克隆的方式來(lái)把對(duì)象進(jìn)行深度拷貝每币。
3携丁、如何對(duì)對(duì)象進(jìn)行深度拷貝
方式一:實(shí)現(xiàn)Cloneable接口
(1)需要拷貝的對(duì)象實(shí)現(xiàn)Cloneable 接口
public classUserimplementsCloneable {privateStringname;privateIntegerage;publicString getName() {return name;
}public voidsetName(String name) {this.name= name;
}publicInteger getAge() {return age;
}public voidsetAge(Integer age) {this.age= age;
}
@OverrideprotectedObject clone()throwsCloneNotSupportedException {return super.clone();
}
@OverridepublicString toString() {return "User{"+"name='"+name+'\''+", age="+age+'}';
}
}
(2)然后我們按上面的流程測(cè)試
public static voidmain(String[] args) {try{
User user1 =newUser();
user1.setName("我是user11");
user1.setAge(188);
List userList =newArrayList();
userList.add(user1);//克隆user1對(duì)象User user2 = (User) userList.get(0).clone();
user2.setAge(11);
user2.setName("我是user22");
System.out.println(user1);
System.out.println(user2);
}catch(Exception e) {
e.printStackTrace();
}
}
(3)打印結(jié)果
注意: 如果User 里有一個(gè)Class 對(duì)象的屬性,此時(shí)需要完全深度克隆的話(huà)兰怠,那么Class這個(gè)類(lèi)也要實(shí)現(xiàn)Cloneable 接口梦鉴,否則只有user其他的屬性值深度拷貝了李茫,但class這個(gè)對(duì)象還是進(jìn)行淺拷貝;
方式二:把對(duì)象進(jìn)行序列化后再反序列化
(1)對(duì)象實(shí)現(xiàn)序列化接口
public classUserimplementsSerializable{privateStringname;privateIntegerage;publicString getName() {return name;
}public voidsetName(String name) {this.name= name;
}publicInteger getAge() {return age;
}public voidsetAge(Integer age) {this.age= age;
}
@OverridepublicString toString() {return "User{"+"name='"+name+'\''+", age="+age+'}';
}
}
(1)把對(duì)象進(jìn)行序列化后再反序列化
public static voidmain(String[] args) {try{
User user1 =newUser();
user1.setName("我是user111");
user1.setAge(1888);//序列化寫(xiě)入到流里ByteOutputStream bots=newByteOutputStream();
ObjectOutputStream oos =newObjectOutputStream(bots);
oos.writeObject(user1);//反序列化成user2對(duì)象ObjectInputStream ois=newObjectInputStream(newByteArrayInputStream(bots.toByteArray()));
User user2 = (User) ois.readObject();
user2.setAge(111);
user2.setName("我是user222");
System.out.println(user1);
System.out.println(user2);
}catch(Exception e) {
e.printStackTrace();
}
}
最后看結(jié)果:
4尚揣、最后找了一個(gè)深度拷貝工具類(lèi)供大家使用
public abstract classBeanUtil {public static T cloneTo(T src)throwsRuntimeException {
ByteArrayOutputStream memoryBuffer =newByteArrayOutputStream();
ObjectOutputStream out =null;
ObjectInputStream in =null;
T dist =null;try{
out =newObjectOutputStream(memoryBuffer);
out.writeObject(src);
out.flush();
in =newObjectInputStream(newByteArrayInputStream(memoryBuffer.toByteArray()));
dist = (T) in.readObject();
}catch(Exception e) {throw newRuntimeException(e);
}finally{if(out !=null) {try{
out.close();
out =null;
}catch(IOException e) {throw newRuntimeException(e);
}
}if(in !=null) {try{
in.close();
in =null;
}catch(IOException e) {throw newRuntimeException(e);
}
}
}returndist;
}
}
,