Java 深拷貝,淺拷貝

一直據說這兩個詞,確實不知道表明啥意思?也不知道究竟要用來作什麼?何時用到他們。html

下面是從一篇博文種獲得的解釋:java

淺複製(淺克隆) :被複制對象的全部變量都含有與原來的對象相同的值,而全部的對其餘對象的引用仍然指向原來的對象。換言之,淺複製僅僅複製所考慮的對象,而不復制它所引用的對象。編程

深複製(深克隆) :被複制對象的全部變量都含有與原來的對象相同的值,除去那些引用其餘對象的變量。那些引用其餘對象的變量將指向被複制過的新對象,而再也不是原有的那些被引用的對象。換言之,深複製把要複製的對象所引用的對象都複製了一遍。數組

在Java語言中,數據類型分爲值類型(基本數據類型)和引用類型,值類型包括int、double、byte、boolean、char等簡單數據類型,引用類型包括類、接口、數組等複雜類型。淺克隆和深克隆的主要區別在因而否支持引用類型的成員變量的複製。less

有兩種方式:(目前還不知道怎麼作)
1). 實現Cloneable接口並重寫Object類中的clone()方法;
2). 實現Serializable接口,經過對象的序列化和反序列化實現克隆,能夠實現真正的深度克隆;ide

實現clone方法的步驟
(1)實現Cloneable接口
(2)重載Object類中的clone()方法,重載時需定義爲public
(3)在重載方法中,調用super.clone()學習

 1 package lesson1211;
 2 
 3 public class Student implements Cloneable {   //不實現Cloneable接口,編譯不會報錯,可是運行時會報異常,因此必須實現Cloneable接口  4     private int number;
 5 
 6     //淺複製
 7     /* (non-Javadoc)
 8      * @see java.lang.Object#clone()
 9      */
10     @Override
11     protected Object clone(){
12         
13         Student stu = null;        
14         try {
15             stu = (Student)super.clone();
16         } catch (CloneNotSupportedException e) {
17             // TODO Auto-generated catch block
18             e.printStackTrace();
19         }        
20         return stu;
21     }
22     
23     /**
24      * @return the number
25      */
26     public int getNumber() {
27         return number;
28     }
29 
30     /**
31      * @param number the number to set
32      */
33     public void setNumber(int number) {
34         this.number = number;
35     }
36 }
37 
38 package lesson1211;
39 
40 public class TestClone {
41 
42     public static void main(String[] args) {
43         Student stu1 = new Student();
44         stu1.setNumber(12345);
45         Student stu2 = (Student)stu1.clone();
46         System.out.println("stu1: " + stu1.getNumber());
47         System.out.println("stu2: " + stu2.getNumber());
48         
49         stu2.setNumber(45678);
50         
51         System.out.println("stu1: " + stu1.getNumber());
52         System.out.println("stu2: " + stu2.getNumber());53         
54     } 
57 }

解釋:
(1)clone()方法是定義在java.lang.Object類中,該方法是一個protected的方法,因此重載時要把clone()方法的屬性設置爲public,這樣其它類才能調用這個clone類的clone()方法
(2)實現Cloneable接口:Cloneable接口是不包含任何方法的!其實這個接口僅僅是一個標誌,並且這個標誌也僅僅是針對Object類中clone()方法的,若是clone類沒有實現Cloneable接口,並調用了Object的clone()方法(也就是調用了super.Clone()方法),那麼Object的clone()方法就會拋出 CloneNotSupportedException異常。this

淺克隆spa

在淺克隆中,若是原型對象的成員變量是值類型,將複製一份給克隆對象;若是原型對象的成員變量是引用類型,則將引用對象的地址複製一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的內存地址。
這裏寫圖片描述
在Java語言中,經過覆蓋Object類的clone()方法能夠實現淺克隆。    能夠看下,原型對象的成員變量是引用類型Car,確實指向同一地址,沒有新城新的引用。.net

  1 package lesson1211;
  2 
  3 public class Student implements Cloneable {
  4     private int number;
  5     private Car car;
  6     
  7     public Student(int number, Car car) {
  8         
  9         this.car = car;
 10         this.number = number;
 11     }
 12     
 13     //淺複製
 14     /* (non-Javadoc)
 15      * @see java.lang.Object#clone()
 16      */
 17     @Override
 18     protected Object clone(){
 19         
 20         Student stu = null;        
 21         try {
 22             stu = (Student)super.clone();
 23         } catch (CloneNotSupportedException e) {
 24             // TODO Auto-generated catch block
 25             e.printStackTrace();
 26         }        
 27         return stu;
 28     }
 29     
 30     /**
 31      * @return the number
 32      */
 33     public int getNumber() {
 34         return number;
 35     }
 36 
 37     /**
 38      * @param number the number to set
 39      */
 40     public void setNumber(int number) {
 41         this.number = number;
 42     }
 43 
 44     /**
 45      * @return the car
 46      */
 47     public Car getCar() {
 48         return car;
 49     }
 50 
 51     /**
 52      * @param car the car to set
 53      */
 54     public void setCar(Car car) {
 55         this.car = car;
 56     }    
 57 }
 58 
 59 package lesson1211;
 60 
 61 public class Car {
 62     
 63     private String name;
 64     
 65     private int speed;
 66     
 67     public Car(String name, int speed) {
 68         this.name = name;
 69         this.speed = speed;
 70     }
 71 
 72     /**
 73      * @return the name
 74      */
 75     public String getName() {
 76         return name;
 77     }
 78 
 79     /**
 80      * @param name the name to set
 81      */
 82     public void setName(String name) {
 83         this.name = name;
 84     }
 85 
 86     /**
 87      * @return the speed
 88      */
 89     public int getSpeed() {
 90         return speed;
 91     }
 92 
 93     /**
 94      * @param speed the speed to set
 95      */
 96     public void setSpeed(int speed) {
 97         this.speed = speed;
 98     }
 99     
100 }
101 package lesson1211;
102 
103 public class TestClone {
104 
105     public static void main(String[] args) {
106         Car car = new Car(null,20000);
107         Student stu1 = new Student(3,car);
108         stu1.setNumber(12345);
109         stu1.getCar().setName("Baoma");
110         
111         Student stu2 = (Student)stu1.clone();
112         System.out.println("stu1: " + stu1.getNumber() + " " + stu1.getCar().getName());
113         System.out.println("stu2: " + stu2.getNumber() + " " + stu2.getCar().getName());
114         
115         stu2.setNumber(45678);
116         stu2.getCar().setName("benchi");
117         
118         System.out.println("stu1: " + stu1.getNumber() + " " + stu1.getCar().getName());
119         System.out.println("stu2: " + stu2.getNumber() + " " + stu2.getCar().getName());120         
121     } 
124 }

運行結果是:

stu1: 12345 Baoma
stu2: 12345 Baoma
stu1: 12345 benchi   //修改stu2的car類型,stu1裏面也會變,說明stu1和stu2裏面的引用car指向同一個地址。這就是淺複製。
stu2: 45678 benchi

深克隆

在深克隆中,不管原型對象的成員變量是值類型仍是引用類型,都將複製一份給克隆對象,深克隆將原型對象的全部引用對象也複製一份給克隆對象。
簡單來講,在深克隆中,除了對象自己被複制外,對象所包含的全部成員變量也將複製。

在Java語言中,若是須要實現深克隆,能夠經過覆蓋Object類的clone()方法實現,也能夠經過序列化(Serialization)等方式來實現。
若是引用類型裏面還包含不少引用類型,或者內層引用類型的類裏面又包含引用類型,使用clone方法就會很麻煩。這時咱們能夠用序列化的方式來實現對象的深克隆。
序列化就是將對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在於內存中。經過序列化實現的拷貝不只能夠複製對象自己,並且能夠複製其引用的成員對象,所以經過序列化將對象寫到一個流中,再從流裏將其讀出來,能夠實現深克隆。須要注意的是可以實現序列化的對象其類必須實現Serializable接口,不然沒法實現序列化操做。

一樣是上面的例子,咱們怎麼來經過Object類的clone來實現深克隆,code以下:

  1 package lesson1211;
  2 
  3 public class Student implements Cloneable {
  4     private int number;
  5     String name;
  6     private Car car;
  7     
  8     public Student(int number, String name, Car car) {
  9         
 10         this.car = car;
 11         this.name = name;
 12         this.number = number;
 13     }
 14     
 15     //淺複製
 16     /* (non-Javadoc)
 17      * @see java.lang.Object#clone()
 18      */
 19     @Override
 20     protected Object clone(){
 21         
 22         Student stu = null;    
 23         try {
 24             stu = (Student)super.clone(); //淺複製            
 25             stu.car = (Car)car.clone();   //深度複製
 26             
 27         } catch (CloneNotSupportedException e) {
 28             // TODO Auto-generated catch block
 29             e.printStackTrace();
 30         }        
 31         return stu;
 32     }
 33     
 34     /**
 35      * @return the number
 36      */
 37     public int getNumber() {
 38         return number;
 39     }
 40 
 41     /**
 42      * @param number the number to set
 43      */
 44     public void setNumber(int number) {
 45         this.number = number;
 46     }
 47 
 48     /**
 49      * @return the car
 50      */
 51     public Car getCar() {
 52         return car;
 53     }
 54 
 55     /**
 56      * @param car the car to set
 57      */
 58     public void setCar(Car car) {
 59         this.car = car;
 60     }
 61 
 62     /**
 63      * @return the name
 64      */
 65     public String getName() {
 66         return name;
 67     }
 68 
 69     /**
 70      * @param name the name to set
 71      */
 72     public void setName(String name) {
 73         this.name = name;
 74     }    
 75 }
 76 
 77 package lesson1211;
 78 
 79 public class Car implements Cloneable {
 80     
 81     private String name;
 82     
 83     private int speed;
 84     
 85     public Car(String name, int speed) {
 86         this.name = name;
 87         this.speed = speed;
 88     }
 89 
 90     /**
 91      * @return the name
 92      */
 93     public String getName() {
 94         return name;
 95     }
 96 
 97     /**
 98      * @param name the name to set
 99      */
100     public void setName(String name) {
101         this.name = name;
102     }
103 
104     /**
105      * @return the speed
106      */
107     public int getSpeed() {
108         return speed;
109     }
110 
111     /**
112      * @param speed the speed to set
113      */
114     public void setSpeed(int speed) {
115         this.speed = speed;
116     }
117 
118     /* (non-Javadoc)
119      * @see java.lang.Object#clone()
120      */
121     @Override
122     public Object clone() {        
123         Car car = null;        
124         try {
125             car =  (Car)super.clone();
126         } catch (CloneNotSupportedException e) {
127             // TODO Auto-generated catch block
128             e.printStackTrace();
129         }        
130         return car;         
131     }    
132 }
133 
134 package lesson1211;
135 
136 public class TestClone {
137 
138     public static void main(String[] args) {
139         Car car = new Car(null,20000);
140         Student stu1 = new Student(3,null, car);
141         stu1.setNumber(12345);
142         stu1.setName("zhangsan");
143         stu1.getCar().setName("Baoma");
144         
145         Student stu2 = (Student)stu1.clone();
146         System.out.println("stu1: " + stu1.getNumber() + " " + stu1.getName()+ " " + stu1.getCar().getName());
147         System.out.println("stu2: " + stu2.getNumber() + " " + stu2.getName()+ " " + stu2.getCar().getName());
148         
149         stu2.setNumber(45678);
150         stu2.setName("zhaosi");
151         stu2.getCar().setName("benchi");
152         
153         System.out.println("stu1: " + stu1.getNumber() + " " + stu1.getName()+ " " + stu1.getCar().getName());
154         System.out.println("stu2: " + stu2.getNumber() + " " + stu2.getName()+ " " + stu2.getCar().getName());
155         
156     }
157 }

 

運行結果:

stu1: 12345 zhangsan Baoma
stu2: 12345 zhangsan Baoma
stu1: 12345 zhangsan Baoma
stu2: 45678 zhaosi benchi      //達到了咱們想要的深度複製結果。

code主要修改的是Car類,它也必須實現Cloneable接口。   Student在實現clone時,單獨在clone 它的引用類型變量。  在student裏面添加一個String變量,發現是深克隆。

除了基本數據類型能自動實現深度clone之外,String對象是一個例外,它clone後的表現好象也實現了深度clone,雖然這只是一個假象,但卻大大方便了咱們的編程。

JDK中StringBuffer類型,關於StringBuffer的說明,StringBuffer沒有重載clone()方法,更爲嚴重的是StringBuffer仍是一個 final類,這也是說咱們也不能用繼承的辦法間接實現StringBuffer的clone。若是一個類中包含有StringBuffer類型對象或和 StringBuffer類似類的對象,咱們有兩種選擇:要麼只能實現影子clone,要麼就在類的clone()方法中加一句(假設是 SringBuffer對象,並且變量名還是p): o.p = new StringBuffer(p.toString()); //原來的是:stu.car = (Car)car.clone();

經過以上咱們能夠看出在某些狀況下,咱們能夠利用clone方法來實現對象的深度複製,但對於比較複雜的對象(好比對象中包含其餘對象,其餘對象又包含別的對象…..)這樣咱們必須進行層層深度clone,每一個對象須要實現cloneable接口,比較麻煩,那就繼續學習下一個序列化方法。

 利用串行化來作深複製

所謂對象序列化就是將對象的狀態轉換成字節流,之後能夠經過這些值再生成相同狀態的對象。

也許你會說,只瞭解一點點,但歷來沒有接觸過,其實未必如此。RMI、Socket、JMS、EJB你總該用過一種吧,彼此爲何可以傳遞Java對象,固然都是對象序列化機制的功勞。

第一次使用Java的對象序列化是作某項目,當時要求把幾棵很是複雜的樹(JTree)及相應的數據保存下來(就是咱們經常使用的保存功能),以便下次運行程序時能夠繼續上次的操做。

那時XML技術在網上很是的熱,並且功能也強大,再加上樹的結構原本就和XML存儲數據的格式很像。做爲一項對新技術比較有興趣的我固然很想嘗試一下。不過通過仔細分析,發現若是採用XML保存數據,後果然是不可思議:哪棵樹的哪一個節點被展開、展開到第幾級、節點當前的屬性是什麼。真是不知該用A、B、C仍是用一、二、3來表示。

還好,發現了Java的對象序列化機制,問題迎刃而解,只需簡單的將每棵樹的根節點序列化保存到硬盤上,下次再經過反序列化後的根節點就能夠輕鬆的構造出和原來如出一轍的樹來。

其實保存數據,尤爲是複雜數據的保存正是對象序列化的典型應用。最近另外一個項目就遇到了須要對很是複雜的數據進行存取,經過使用對象的序列化,問題一樣化難爲簡。

上面這段是摘抄於帖子:https://blog.csdn.net/pony_maggie/article/details/52091588,我本身目前固然沒作過。

對象的序列化還有另外一個容易被你們忽略的功能就是對象複製(Clone),Java中經過Clone機制能夠複製大部分的對象,可是衆所周知,Clone有深層Clone和淺層Clone,若是你的對象很是很是複雜,假設有個100層的Collection(誇張了點),若是你想實現深層 Clone,真是不敢想象,若是使用序列化,不會超過10行代碼就能夠解決。


這樣作的前提是對象以及對象內部全部引用到的對象都是可串行化的,不然,
就須要仔細考察那些不可串行化的對象或屬性能否設成transient,從而將之排除在複製過程以外。 上例代碼修改以下: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()); }
  1 package lesson1211;
  2 
  3 import java.io.ByteArrayInputStream;
  4 import java.io.ByteArrayOutputStream;
  5 import java.io.IOException;
  6 import java.io.ObjectInputStream;
  7 import java.io.ObjectOutputStream;
  8 import java.io.Serializable;
  9 
 10 public class Student implements Serializable{
 11     private int number;
 12     String name;
 13     private Car car;
 14     
 15     public Student(int number, String name, Car car) {
 16         
 17         this.car = car;
 18         this.name = name;
 19         this.number = number;
 20     }
 21     
 22     public Object deepClone() throws IOException, ClassNotFoundException{
 23         ByteArrayOutputStream bo = new ByteArrayOutputStream();
 24         ObjectOutputStream oo = new ObjectOutputStream(bo);
 25         oo.writeObject(this);
 26         
 27         ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
 28         ObjectInputStream oi = new ObjectInputStream(bi);
 29         return oi.readObject();        
 30         
 31     }
 32     
 33     /**
 34      * @return the number
 35      */
 36     public int getNumber() {
 37         return number;
 38     }
 39 
 40     /**
 41      * @param number the number to set
 42      */
 43     public void setNumber(int number) {
 44         this.number = number;
 45     }
 46 
 47     /**
 48      * @return the car
 49      */
 50     public Car getCar() {
 51         return car;
 52     }
 53 
 54     /**
 55      * @param car the car to set
 56      */
 57     public void setCar(Car car) {
 58         this.car = car;
 59     }
 60 
 61     /**
 62      * @return the name
 63      */
 64     public String getName() {
 65         return name;
 66     }
 67 
 68     /**
 69      * @param name the name to set
 70      */
 71     public void setName(String name) {
 72         this.name = name;
 73     }    
 74 }
 75 
 76 package lesson1211;
 77 
 78 import java.io.Serializable;
 79 
 80 public class Car implements Serializable {
 81     
 82     private String name;
 83     
 84     private int speed;
 85     
 86     public Car(String name, int speed) {
 87         this.name = name;
 88         this.speed = speed;
 89     }
 90 
 91     /**
 92      * @return the name
 93      */
 94     public String getName() {
 95         return name;
 96     }
 97 
 98     /**
 99      * @param name the name to set
100      */
101     public void setName(String name) {
102         this.name = name;
103     }
104 
105     /**
106      * @return the speed
107      */
108     public int getSpeed() {
109         return speed;
110     }
111 
112     /**
113      * @param speed the speed to set
114      */
115     public void setSpeed(int speed) {
116         this.speed = speed;
117     }
118     
119 }
120 
121 package lesson1211;
122 
123 import java.io.IOException;
124 
125 public class TestClone {
126 
127     public static void main(String[] args) throws IOException, ClassNotFoundException{
128         Car car = new Car(null,20000);
129         Student stu1 = new Student(3,null, car);
130         stu1.setNumber(12345);
131         stu1.setName("zhangsan");
132         stu1.getCar().setName("Baoma");
133         
134         //Student stu2 = (Student)stu1.clone();
135         Student stu2 = (Student)stu1.deepClone();
136         System.out.println("stu1: " + stu1.getNumber() + " " + stu1.getName()+ " " + stu1.getCar().getName());
137         System.out.println("stu2: " + stu2.getNumber() + " " + stu2.getName()+ " " + stu2.getCar().getName());
138         
139         stu2.setNumber(45678);
140         stu2.setName("zhaosi");
141         stu2.getCar().setName("benchi");
142         
143         System.out.println("stu1: " + stu1.getNumber() + " " + stu1.getName()+ " " + stu1.getCar().getName());
144         System.out.println("stu2: " + stu2.getNumber() + " " + stu2.getName()+ " " + stu2.getCar().getName());
145         
146     }
147     
148 
149 }

運行結果:

stu1: 12345 zhangsan Baoma
stu2: 12345 zhangsan Baoma
stu1: 12345 zhangsan Baoma
stu2: 45678 zhaosi benchi
確實也實現了深度複製。看上去感受比clone還簡單

兩個問題:1 引用類型類可不能夠不實現Serializable?例如Car類不實現Serializable?    不能夠,必須實現,不然會拋異常

2:若是引用類型是transient,就不能進行序列化?   嘗試將private Car car;改成private transient Car car;  在上面例子中會報錯,由於序列化進去的Car是null, 後面getCar.getName()會報空指針的。對於沒有實現Serializable接口的,序列化時要主動將其定義爲transient,只要後面不在調用這種類引用就能夠。

雖然Java的序列化很是簡單、強大,可是要用好,還有不少地方須要注意。好比曾經序列化了一個對象,可因爲某種緣由,該類作了一點點改動,而後從新被編譯,那麼這時反序列化剛纔的對象,將會出現異常。

你能夠經過添加serialVersionUID屬性來解決這個問題。若是你的類是個單態(Singleton)類,是否容許用戶經過序列化機制複製該類,若是不容許你須要謹慎對待該類的實現。

 

https://blog.csdn.net/pony_maggie/article/details/52091588

https://blog.csdn.net/w410589502/article/details/54985987

https://blog.csdn.net/tounaobun/article/details/8491392

https://www.cnblogs.com/dolphin0520/p/3700693.html

相關文章
相關標籤/搜索