java的數據類型有基本數據類型(如:int、long等)和引用數據類型。例如:對象1中有屬性a(基本數據類型)和屬性b(引用數據類型),在進行淺拷貝到對象2時,屬性a複製屬性的值給對象2,這樣對象1和對象2修改屬性a時不會相互影響。屬性b則是複製一份引用(即內存地址)給對象2,這樣對象1修改屬性b後,對象2中的值也會改變。java
通常經過Object的clone( )方法來實現淺複製,使用clone( )方法須要實現Cloneable接口。ide
clone( )方法的基本規則以下:學習
一、基本數據類型this
進行值的拷貝,例如:int、long、byte等。spa
二、Stringcode
若是變量是String類型,則拷貝其地址引用,可是由於java中String是不變的,因此在修改的時候是新建一個String,這樣新對象指向新地址不影響舊對象的值。對象
三、對象接口
對象的拷貝是複製其引用地址,即新對象和原來對象共用一個對象實例。內存
package com.hs.copy; public class Age { private int age; public Age(int age) { super(); this.age = age; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Age [age=" + age + "]"; } /** * @return the age */ public int getAge() { return age; } /** * @param age the age to set */ public void setAge(int age) { this.age = age; } }
package com.hs.copy; public class Person implements Cloneable{ private String name; private Age age; public Person(String name, int age) { super(); this.name = name; this.age = new Age(age); } public Person clone(){ try { return (Person)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Person [name=" + name + ", age=" + age.getAge() + "]"; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the age */ public Age getAge() { return age; } /** * @param age the age to set */ public void setAge(Age age) { this.age = age; } }
package com.hs.copy; /** * 淺拷貝 * @author Administrator * */ public class ShallowCopy { public static void main(String[] args) { Person person1 = new Person("yang", 18); System.out.println( "person1 : " + person1.toString() ); Person person2 = person1.clone(); System.out.println( "person2 : " + person2.toString() ); person2.setName("yyyy"); System.out.println(); System.out.println( "-----修改基本類型變量後-----"); System.out.println( "person1 : " + person1.toString() ); System.out.println( "person2 : " + person2.toString() ); person2.getAge().setAge(20); System.out.println(); System.out.println( "-----修改引用類型變量後-----"); System.out.println( "person1 : " + person1.toString() ); System.out.println( "person2 : " + person2.toString() ); } }
運行結果:get
person1 : Person [name=yang, age=18] person2 : Person [name=yang, age=18] -----修改基本類型變量後----- person1 : Person [name=yang, age=18] person2 : Person [name=yyyy, age=18] -----修改引用類型變量後----- person1 : Person [name=yang, age=20] person2 : Person [name=yyyy, age=20]
經過上面的學習,很容易發現淺拷貝有個缺陷,即變量是引用類型時並無實現值的拷貝。這時候就要用到深拷貝來實現了。以下是深拷貝的兩種實現。
1、將引用類型的變量以及變量的變量所有實現clone( )方法
該實現的缺點:當引用類型的變量不少、很深時,代碼量很大
package com.hs.copy; public class Age implements Cloneable{ private int age; public Age(int age) { super(); this.age = age; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Age [age=" + age + "]"; } /** * 深拷貝-新增 */ public Age clone(){ try { return (Age)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } /** * @return the age */ public int getAge() { return age; } /** * @param age the age to set */ public void setAge(int age) { this.age = age; } }
package com.hs.copy; public class Person implements Cloneable{ private String name; private Age age; public Person(String name, int age) { super(); this.name = name; this.age = new Age(age); } /** * 深拷貝 */ public Person clone(){ try { Person person = (Person)super.clone(); Age age = person.age.clone(); person.setAge(age); return person; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } /** * 淺拷貝 */ /*public Person clone(){ try { return (Person)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; }*/ /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Person [name=" + name + ", age=" + age.getAge() + "]"; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the age */ public Age getAge() { return age; } /** * @param age the age to set */ public void setAge(Age age) { this.age = age; } }
package com.hs.copy; /** * 深拷貝 * @author Administrator * */ public class DeepCopy { public static void main(String[] args) { Person person1 = new Person("yang", 18); System.out.println( "person1 : " + person1.toString() ); Person person2 = person1.clone(); System.out.println( "person2 : " + person2.toString() ); person2.setName("yyyy"); System.out.println(); System.out.println( "-----修改基本類型變量後-----"); System.out.println( "person1 : " + person1.toString() ); System.out.println( "person2 : " + person2.toString() ); person2.getAge().setAge(20); System.out.println(); System.out.println( "-----修改引用類型變量後-----"); System.out.println( "person1 : " + person1.toString() ); System.out.println( "person2 : " + person2.toString() ); } }
結果:
person1 : Person [name=yang, age=18] person2 : Person [name=yang, age=18] -----修改基本類型變量後----- person1 : Person [name=yang, age=18] person2 : Person [name=yyyy, age=18] -----修改引用類型變量後----- person1 : Person [name=yang, age=18] person2 : Person [name=yyyy, age=20]
2、經過對象的序列化,再反序列化實現深拷貝
package com.hs.copy; import java.io.Serializable; public class AgeDeep implements Serializable{ private int age; public AgeDeep(int age) { super(); this.age = age; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Age [age=" + age + "]"; } /** * @return the age */ public int getAge() { return age; } /** * @param age the age to set */ public void setAge(int age) { this.age = age; } }
package com.hs.copy; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class PersonDeep implements Serializable { private String name; private AgeDeep age; public PersonDeep(String name, int age) { super(); this.name = name; this.age = new AgeDeep(age); } public PersonDeep deepClone(){ ObjectOutputStream objectOutputStream = null; ObjectInputStream objectInputStream = null; try { //序列化 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); //反序列化 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); objectInputStream = new ObjectInputStream(byteArrayInputStream); return (PersonDeep)objectInputStream.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); }finally { try { if( objectOutputStream != null ){ objectOutputStream.close(); } if( objectInputStream != null ){ objectInputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return null; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Person [name=" + name + ", age=" + age.getAge() + "]"; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the age */ public AgeDeep getAge() { return age; } /** * @param age the age to set */ public void setAge(AgeDeep age) { this.age = age; } }
package com.hs.copy; /** * 深拷貝 * @author Administrator * */ public class DeepCopy { /** * 序列化深拷貝 * @param args */ public static void main(String[] args) { PersonDeep person1 = new PersonDeep("yang", 18); System.out.println( "person1 : " + person1.toString() ); PersonDeep person2 = person1.deepClone(); System.out.println( "person2 : " + person2.toString() ); person2.setName("yyyy"); System.out.println(); System.out.println( "-----修改基本類型變量後-----"); System.out.println( "person1 : " + person1.toString() ); System.out.println( "person2 : " + person2.toString() ); person2.getAge().setAge(20); System.out.println(); System.out.println( "-----修改引用類型變量後-----"); System.out.println( "person1 : " + person1.toString() ); System.out.println( "person2 : " + person2.toString() ); } }
兩種深拷貝效率對比結果:用clone實現明顯快於序列化
序列化: count:10000 time:807ms count:10000 time:835ms count:10000 time:813ms count:100000 time:1894ms count:100000 time:1926ms count:100000 time:1973ms count:1000000 time:11313ms count:1000000 time:10930ms count:1000000 time:11016ms clone(): count:10000 time:3ms count:10000 time:7ms count:10000 time:4ms count:100000 time:13ms count:100000 time:16ms count:100000 time:14ms count:1000000 time:43ms count:1000000 time:38ms count:1000000 time:40ms