⑴ 淺拷貝(淺克隆)java
複製出來的對象的全部變量都含有與原來的對象相同的值,而全部的對其餘對象的引用仍然指向原來的對象。this
⑵ 深拷貝(深克隆)spa
複製出來的全部變量都含有與原來的對象相同的值,那些引用其餘對象的變量將指向複製出來的新對象,而再也不是原有的那些被引用的對象。換言之,深複製把要複製的對象所引用的對象都複製了一遍。code
(1)clone方法將對象複製了一份並返回給調用者。通常而言,clone()方法知足: //克隆對象與原對象不是同一個對象 ①對任何的對象x,都有x.clone() !=x //克隆對象與原對象的類型同樣 ②對任何的對象x,都有x.clone().getClass()= =x.getClass() ③若是對象x的equals()方法定義恰當,那麼x.clone().equals(x)應該成立。 (2)Java中對象的克隆 ①爲了獲取對象的一份拷貝,咱們能夠利用Object類的clone()方法 ②在派生類中覆蓋基類的clone()方法,並聲明爲public ③在派生類的clone()方法中,調用super.clone() ④在派生類中實現Cloneable接口
說明:
①爲何咱們在派生類中覆蓋Object的clone()方法時,必定要調用super.clone()呢?對象
在運行時刻,Object中的clone()識別出你要複製的是哪個對象,而後爲此對象分配空間,並進行對象的複製,將原始對象的內容一一複製到新對象的存儲空間中。繼承
②繼承自java.lang.Object類的clone()方法是淺拷貝。接口
(1)無引用克隆get
class Student implements Cloneable { String name; int age; Student(String name, int age) { this.name = name; this.age = age; } public Object clone() { Object o = null; try { o = (Student) super.clone();//Object 中的clone()識別出你要複製的是哪個對象。 } catch (CloneNotSupportedException e) { System.out.println(e.toString()); } return o; } } public class CloneDemo { public static void main(String[] args) { Student s1 = new Student("zhangsan", 18); Student s2 = (Student) s1.clone(); s2.name = "lisi"; s2.age = 20; //修改學生2後,不影響學生1的值。 System.out.println("name=" + s1.name + "," + "age=" + s1.age); System.out.println("name=" + s2.name + "," + "age=" + s2.age); } }
運行結果it
name=zhangsan,age=18 name=lisi,age=20
(2)有引用克隆io
class Professor { String name; int age; Professor(String name, int age) { this.name = name; this.age = age; } } class Student implements Cloneable { String name;// 常量對象。 int age; Professor p;// 學生1和學生2的引用值都是同樣的。 Student(String name, int age, Professor p) { this.name = name; this.age = age; this.p = p; } public Object clone() { Student o = null; try { o = (Student) super.clone(); } catch (CloneNotSupportedException e) { System.out.println(e.toString()); } return o; } } public class CloneDemo { public static void main(String[] args) { Professor p = new Professor("wangwu", 50); Student s1 = new Student("zhangsan", 18, p); Student s2 = (Student) s1.clone(); s2.p.name = "lisi"; s2.p.age = 30; System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); System.out.println("name=" + s2.p.name + "," + "age=" + s2.p.age); //輸出結果學生1和2的教授成爲lisi,age爲30。 } }
運行結果
name=lisi,age=30 name=lisi,age=30
(3)實例(2)的深層次改進
//引用一樣實現Cloneable接口 class Professor implements Cloneable { String name; int age; Professor(String name, int age) { this.name = name; this.age = age; } public Object clone() { Object o = null; try { o = super.clone(); } catch (CloneNotSupportedException e) { System.out.println(e.toString()); } return o; } } class Student implements Cloneable { String name;// 常量對象。 int age; Professor p;// 學生1和學生2的引用值都是同樣的。 Student(String name, int age, Professor p) { this.name = name; this.age = age; this.p = p; } public Object clone() { Student o = null; try { o = (Student) super.clone(); } catch (CloneNotSupportedException e) { System.out.println(e.toString()); } //對引用的對象也進行復制 o.p = (Professor) p.clone(); return o; } } public class CloneDemo { public static void main(String[] args) { Professor p = new Professor("wangwu", 50); Student s1 = new Student("zhangsan", 18, p); Student s2 = (Student) s1.clone(); s2.p.name = "lisi"; s2.p.age = 30; System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); System.out.println("name=" + s2.p.name + "," + "age=" + s2.p.age); //輸出結果學生1和2的教授成爲lisi,age爲30。 } }
運行結果
name=wangwu,age=50 name=lisi,age=30
主要是爲了不重寫比較複雜對象的深拷貝的clone()方法,也能夠程序實現斷點續傳等等功能。把對象寫到流裏的過程是序列化(Serilization)過程,可是在Java程序師圈子裏又很是形象地稱爲「冷凍」或者「醃鹹菜(picking)」過程;而把對象從流中讀出來的並行化(Deserialization)過程則叫作 「解凍」或者「回鮮(depicking)」過程。
應當指出的是,寫在流裏的是對象的一個拷貝,而原對象仍然存在於JVM裏面,所以「醃鹹菜」的只是對象的一個拷貝,Java鹹菜還能夠回鮮。
在Java語言裏深複製一個對象,經常能夠先使對象實現Serializable接口,而後把對象(實際上只是對象的一個拷貝)寫到一個流裏,再從流裏讀出來,即可以重建對象。
以下爲深拷貝源代碼:
public Object deepClone() { //將對象寫到流裏 ByteArrayOutoutStream bo=new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this); //從流裏讀出來 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return(oi.readObject()); }
這樣作的前提是對象以及對象內部全部引用到的對象都是可序列化的,不然,就須要仔細考察那些不可序列化的對象或屬性能否設成transient,從而將之排除在複製過程以外。
將實例中的代碼用序列化改進以下:
//將Professor序列化 class Professor implements Serializable { String name; int age; Professor(String name, int age) { this.name = name; this.age = age; } } //將Student序列化 class Student implements Serializable { String name;// 常量對象。 int age; Professor p;// 學生1和學生2的引用值都是同樣的。 Student(String name, int age, Professor p) { this.name = name; this.age = age; this.p = p; } //深拷貝 public Object deepClone() throws IOException, OptionalDataException, ClassNotFoundException {//將對象寫到流裏 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this);//從流裏讀出來 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return (oi.readObject()); } } public class CloneDemo { public static void main(String[] args) { Professor p = new Professor("wangwu", 50); Student s1 = new Student("zhangsan", 18, p); Student s2 = null; try { s2 = (Student) s1.deepClone(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } s2.p.name = "lisi"; s2.p.age = 30; System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); System.out.println("name=" + s2.p.name + "," + "age=" + s2.p.age); //輸出結果學生1和2的教授成爲lisi,age爲30。 } }</code>
運行結果
name=wangwu,age=50 name=lisi,age=30