Java面向物件3-深拷貝與淺拷貝
阿新 • • 發佈:2022-05-05
深拷貝和淺拷貝最根本的區別在於是否真正獲取一個物件的複製實體,而不是引用。
假設B複製了A,修改A的時候,看B是否發生變化:
如果B跟著也變了,說明是淺拷貝,拿人手短!(修改堆記憶體中的同一個值)
如果B沒有改變,說明是深拷貝,自食其力!(修改堆記憶體中的不同的值)
1、淺拷貝
在拷貝一個物件時,對物件的基本資料型別的成員變數進行拷貝,但對引用型別的成員變數只進行引用的傳遞,並沒有建立一個新的物件,當對引用型別的內容修改會影響被拷貝的物件。
只是增加了一個指標指向已存在的記憶體地址,java中clone方法是一個淺拷貝,引用型別依然在傳遞引用。
如果克隆物件的子物件是不可變的,或者子物件沒有更改器方法,那麼就是安全的
2、深拷貝
除了對基本資料型別的成員變數進行拷貝,對引用型別的成員變數進行拷貝時,建立一個新的物件來儲存引用型別的成員變數。
增加了一個指標並且申請了一個新的記憶體,使這個增加的指標指向這個新的記憶體。
重新定義clone方法,克隆出所有子物件
Cloneable接口出現和介面的使用沒有任何關係,因為clone方法是Object類繼承而來的
Cloneable介面是標記介面,不含任何方法,唯一的作用就是允許在型別查詢中視同instanceof
實現深拷貝有兩種方法:
(1)序列化該物件,然後反序列化回來,就能得到一個新的物件了。
序列化:將物件寫入到IO流中; 反序列化:從IO流中恢復物件 序列化機制允許將實現序列化的java物件轉化為位元組序列,這些位元組序列可以儲存到磁碟或者網路傳輸上,以達到以後恢復成原來的物件,序列化機制使得物件可以脫離程式的執行而獨立存在。
(2)繼續利用clone()方法,對該物件的引用型別變數再實現一次clone()方法。
(1)序列化
1 public class Student3 implements Serializable,Cloneable{ 2 private static final long serialVersionUID = 3462139480068147262L; 3 private Integer age; 4 private String name; 5 6 public Student3(Integer age, String name) { 7this.age = age; 8 this.name = name; 9 } 10 11 public Integer getAge() { 12 return age; 13 } 14 15 public void setAge(Integer age) { 16 this.age = age; 17 } 18 19 public String getName() { 20 return name; 21 } 22 23 public void setName(String name) { 24 this.name = name; 25 } 26 27 @Override 28 protected Object clone() throws CloneNotSupportedException { 29 return super.clone(); 30 } 31 32 public static void main(String[] args) throws CloneNotSupportedException { 33 File file = new File("D:/test.txt"); 34 Student3 stu = new Student3(18, "xiaoxian"); 35 36 System.out.println("clone方法是淺拷貝"); 37 Student3 clone = (Student3)stu.clone(); 38 System.out.println("clone == stu的結果:"+ (clone==stu)); 39 System.out.println("clone.name == stu.name的結果:"+ (clone.name==stu.name)); 40 41 System.out.println("將物件序列化是深拷貝"); 42 //將物件序列化到IO流中 43 try { 44 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file)); 45 objectOutputStream.writeObject(stu); 46 objectOutputStream.close(); 47 } catch (IOException e) { 48 e.printStackTrace(); 49 } 50 51 //將物件從IO流中反序列化出來 52 try { 53 ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file)); 54 Student3 student3 = (Student3) objectInputStream.readObject(); 55 System.out.println("student3 == stu的結果:"+(stu == student3)); 56 System.out.println("student3.name == stu.name的結果:"+(stu.name == student3.name)); 57 } catch (Exception e) { 58 e.printStackTrace(); 59 } 60 } 61 62 }
(2)重寫clone方法
1 package com.company.DeepCopy; 2 3 public class DeepCopy { 4 5 6 public static void main(String[] args) { 7 Age a = new Age(20); 8 Student stu1 = new Student("寧採臣",a,174); 9 10 //通過呼叫重寫後的clone方法進行淺拷貝 11 Student stu2 = (Student)stu1.clone(); 12 System.out.println(stu1.toString()); 13 System.out.println(stu2.toString()); 14 System.out.println(); 15 16 //嘗試修改stu1中的各屬性,觀察stu2的屬性有沒有變化 17 stu1.setName("聶小倩"); 18 //改變age這個引用型別的成員變數的值 19 a.setAge(18); 20 //stu1.setaAge(new Age(99)); 使用這種方式修改age屬性值的話,stu2是不會跟著改變的。因為建立了一個新的Age類物件而不是改變原物件的例項值 21 stu1.setLength(157); 22 System.out.println(stu1.toString()); 23 System.out.println(stu2.toString()); 24 } 25 }
1 package com.company.DeepCopy; 2 3 public class Student implements Cloneable { 4 //學生類的成員變數(屬性),其中一個屬性為類的物件 5 private String name; 6 private Age aage; 7 private int length; 8 //構造方法,其中一個引數為另一個引數的物件 9 public Student(String name,Age a,int length){ 10 this.name = name; 11 this.aage = a; 12 this.length = length; 13 } 14 public String getName() { 15 return name; 16 } 17 public void setName(String name) { 18 this.name = name; 19 } 20 public Age getAge() { 21 return this.aage; 22 } 23 public void setAge(Age age) { 24 this.aage = age; 25 } 26 public int getLength() { 27 return this.length; 28 } 29 public void setLength(int length) { 30 this.length = length; 31 } 32 public String toString(){ 33 return "姓名是:"+this.getName()+",年齡為:"+this.getAge().toString()+",長度是:"+this.getLength(); 34 } 35 //重寫Object類的clone方法 36 public Object clone(){ 37 Object obj = null; 38 //呼叫Object類的clone方法---淺拷貝 39 try { 40 obj = super.clone(); 41 }catch (CloneNotSupportedException e){ 42 e.printStackTrace(); 43 } 44 //呼叫Age類的clone方法進行深拷貝 45 //先將obj轉化為學生類例項 46 Student stu = (Student)obj; 47 //學生類例項的Age物件屬性,呼叫其clone方法進行拷貝 48 stu.aage=(Age)stu.getAge().clone(); 49 return obj; 50 } 51 }
1 package com.company.DeepCopy; 2 3 public class Age implements Cloneable{ 4 //年齡類的成員變數(屬性) 5 private int age; 6 //構造方法 7 public Age(int age){ 8 this.age = age; 9 } 10 public int getAge(){ 11 return age; 12 } 13 public void setAge(int age) { 14 this.age = age; 15 } 16 @Override 17 public String toString() { 18 return this.age+""; 19 } 20 //重寫Object的clone方法 21 public Object clone() { 22 Object obj = null; 23 try { 24 obj = super.clone(); 25 } catch (CloneNotSupportedException e) { 26 e.printStackTrace(); 27 } 28 return obj; 29 } 30 }