詳解java中的深拷貝和淺拷貝(clone()方法的重寫、使用序列化實(shí)現(xiàn)真正的深拷貝)
1.序列化實(shí)現(xiàn)
public class CloneUtils { @SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T object){ T cloneObj = null; try { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obs = new ObjectOutputStream(out); obs.writeObject(object); obs.close(); ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(ios); cloneObj = (T) ois.readObject(); }catch (Exception e){ e.printStackTrace(); } return cloneObj; } }
2.主代碼
public class TestString { public static void main(String[] args) { TestString test = new TestString(); System.out.println("-------淺拷貝---------"); test.qianCopyTest(); System.out.println(); System.out.println("--------使用clone深拷貝--------"); test.defaultCloneTest(); System.out.println(); System.out.println("--------使用序列化實(shí)現(xiàn)對(duì)象的拷貝--------"); test.streamClonrTest(); System.out.println("--------耗時(shí)對(duì)比--------"); System.out.println("耗時(shí)1 : "+ test.qianCopyCost()); System.out.println("耗時(shí)2 : "+ test.CloneCopyCost()); System.out.println("耗時(shí)3 : "+ test.StreamCopyCost()); } /*淺拷貝*/ private void qianCopyTest() { String s = "cd"; change(s); System.out.println(s); System.out.println("----------------"); String b = new String("cd"); change(b); System.out.println(b); System.out.println("----------------"); int me = 1; change(me); System.out.println(me); System.out.println("----------------"); Person person = new Person("我", 13,new Email("我")); change(person); System.out.println(person.toString()); } /*使用默認(rèn)的clone方法,需要Person實(shí)現(xiàn)Cloneable接口*/ private void defaultCloneTest(){ Person person = new Person("我", 13,new Email("我")); Person person1 = person.clone(); Person person2 = person.clone(); System.out.println("person : 【"+person+"】"); System.out.println("person1 : 【"+person1+"】"); System.out.println("person2 : 【"+person2+"】"); //改一個(gè)就會(huì)觸動(dòng)全部!! 這就是使用默認(rèn)的clone方法的弊端 /*該clone()方法是使用Object類的clone()方法,但是該方法存在一個(gè)缺陷,它并不會(huì)將對(duì)象的所有屬性全部拷貝過(guò)來(lái),而是有選擇性的拷貝,基本規(guī)則如下: 1、 基本類型 如果變量是基本很類型,則拷貝其值,比如int、float等。 2、 對(duì)象 如果變量是一個(gè)實(shí)例對(duì)象,則拷貝其地址引用,也就是說(shuō)此時(shí)新對(duì)象與原來(lái)對(duì)象是公用該實(shí)例變量。 3、 String字符串 若變量為String字符串,則拷貝其地址引用。但是在修改時(shí),它會(huì)從字符串池中重新生成一個(gè)新的字符串,原有紫都城對(duì)象保持不變。*/ person.getEmail().setContent("你"); System.out.println("之后的person : 【"+person+"】"); System.out.println("之后的person1 : 【"+person1+"】"); System.out.println("之后的person2 : 【"+person2+"】"); } /*使用序列化實(shí)現(xiàn)對(duì)象的拷貝,需要對(duì)象以及對(duì)象中的其他對(duì)象都要實(shí)現(xiàn)Serializable接口*/ private void streamClonrTest(){ Person person = new Person("我", 13,new Email("我")); Person person1 = CloneUtils.clone(person); Person person2 = CloneUtils.clone(person); System.out.println("person : 【"+person+"】"); System.out.println("person1 : 【"+person1+"】"); System.out.println("person2 : 【"+person2+"】"); person.getEmail().setContent("你"); System.out.println("之后的person : 【"+person+"】"); System.out.println("之后的person1 : 【"+person1+"】"); System.out.println("之后的person2 : 【"+person2+"】"); } private static void change(String x) { x = "ab"; } private static void change(int x) { x = 2; } private static void change(Person x) { x = new Person("你", 20, new Email("你")); } private long qianCopyCost(){ long start = System.currentTimeMillis(); Person person = new Person("我", 13,new Email("我")); List<Person> list = new ArrayList<>(); for(int i = 0;i<=10000;i++){ list.add(new Person("你", 20, new Email("你"))); } return System.currentTimeMillis()-start; } private long CloneCopyCost(){ long start = System.currentTimeMillis(); Person person = new Person("我", 13,new Email("我")); List<Person> list = new ArrayList<>(); for(int i = 0;i<=10000;i++){ list.add(person.clone()); } return System.currentTimeMillis()-start; } private long StreamCopyCost(){ long start = System.currentTimeMillis(); Person person = new Person("我", 13,new Email("我")); List<Person> list = new ArrayList<>(); for(int i = 0;i<=10000;i++){ list.add(CloneUtils.clone(person)); } return System.currentTimeMillis()-start; } } class Person implements Serializable, Cloneable { private static final long serialVersionUID = -8584225043397465132L; private String name; private int age; public void setEmail(Email email) { this.email = email; } private Email email; public Email getEmail() { return email; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public Person(String name, int age, Email email) { this.name = name; this.age = age; this.email = email; } @Override public String toString() { return "name : " + name + " | age : " + age +" | content : "+email.getContent(); } @Override protected Person clone() { Person person = null; try { person = (Person) super.clone(); /*如果加上下一行 “使用clone深拷貝” 就不會(huì)改一處其他都改變了*/ person.setEmail(new Email(person.getEmail().getContent())); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return person; } } class Email implements Serializable { private static final long serialVersionUID = 1426052929769365539L; private String content; public void setContent(String content) { this.content = content; } public String getContent() { return content; } public Email(String content) { this.content = content; } }
測(cè)試了一下時(shí)間:
輸出:
-------淺拷貝---------
cd
cd
1
name : 我 | age : 13 | content : 我
--------使用clone深拷貝--------
person : 【name : 我 | age : 13 | content : 我】
person1 : 【name : 我 | age : 13 | content : 我】
person2 : 【name : 我 | age : 13 | content : 我】
之后的person : 【name : 我 | age : 13 | content : 你】
之后的person1 : 【name : 我 | age : 13 | content : 我】
之后的person2 : 【name : 我 | age : 13 | content : 我】
--------使用序列化實(shí)現(xiàn)對(duì)象的拷貝--------
person : 【name : 我 | age : 13 | content : 我】
person1 : 【name : 我 | age : 13 | content : 我】
person2 : 【name : 我 | age : 13 | content : 我】
之后的person : 【name : 我 | age : 13 | content : 你】
之后的person1 : 【name : 我 | age : 13 | content : 我】
之后的person2 : 【name : 我 | age : 13 | content : 我】
--------耗時(shí)對(duì)比--------
耗時(shí)1 : 2
耗時(shí)2 : 1
耗時(shí)3 : 338
以上所述是小編給大家介紹的java中的深拷貝和淺拷貝(clone()方法的重寫、使用序列化實(shí)現(xiàn)真正的深拷貝)詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Java面試題及答案集錦(基礎(chǔ)題122道,代碼題19道)
本文是小編收集整理的關(guān)于java基礎(chǔ)面試題及答案集錦,基礎(chǔ)題目有122道,代碼題目有19道,非常不錯(cuò),值得收藏,需要的朋友參考下2017-01-01Java面試題沖刺第二十六天--實(shí)戰(zhàn)編程2
這篇文章主要為大家分享了最有價(jià)值的三道java實(shí)戰(zhàn)編程的面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-08-08SpringMVC如何域?qū)ο蠊蚕頂?shù)據(jù)
在Spring MVC中,可以使用域?qū)ο髞?lái)共享數(shù)據(jù),域?qū)ο笫且粋€(gè)Map類型的對(duì)象,可以在請(qǐng)求處理方法之間共享數(shù)據(jù),本文給大家介紹SpringMVC 域?qū)ο蠊蚕頂?shù)據(jù)的示例代碼,一起看看吧2023-09-09Java 給PDF簽名時(shí)添加可信時(shí)間戳的方法
這篇文章主要介紹了Java 給PDF簽名時(shí)添加可信時(shí)間戳,關(guān)于jar導(dǎo)入的問(wèn)題,本文給大家?guī)?lái)兩種方法,一種是手動(dòng)導(dǎo)入另一種是maven配置導(dǎo)入,需要的朋友可以參考下2021-07-07解決websocket 報(bào) Could not decode a text frame as UTF-8錯(cuò)誤
這篇文章主要介紹了解決websocket 報(bào) Could not decode a text frame as UTF-8錯(cuò)誤,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10