淺拷貝與深拷貝

1. 簡介java

在java中除了基本的數據類型外, 還存在類實例對象的引用數據類型。在使用「=」做賦值操做時,對於基本數據類型,拷貝的是它的值;express

對於引用數據類型,拷貝的是對這個對象的引用,拷貝對象與原對象仍然指向了同一對象。淺拷貝與深拷貝是在上述基礎上進行區分。ide

在拷貝對象的過程當中,若是對其基本數據類型進行拷貝,但對其引用數據類型只進行了引用傳遞(即沒有建立新的對象),則認爲該拷ui

貝是淺拷貝(shallow copy);在拷貝對象的過程當中,若是對其基本數據類型拷貝,且在拷貝其引用數據類型時建立了新對象,則認this

爲該拷貝是深拷貝(deep copy)spa

2. 示例3d

下面展現了clone方法和Cloneable接口。code

 1 // Object.java
 2     /**
 3      * Creates and returns a copy of this object.  The precise meaning
 4      * of "copy" may depend on the class of the object. The general
 5      * intent is that, for any object {@code x}, the expression:
 6      * <blockquote>
 7      * <pre>
 8      * x.clone() != x</pre></blockquote>
 9      * will be true, and that the expression:
10      * <blockquote>
11      * <pre>
12      * x.clone().getClass() == x.getClass()</pre></blockquote>
13      * will be {@code true}, but these are not absolute requirements.
14      * While it is typically the case that:
15      * <blockquote>
16      * <pre>
17      * x.clone().equals(x)</pre></blockquote>
18      * will be {@code true}, this is not an absolute requirement.
19      * <p>
20      * By convention, the returned object should be obtained by calling
21      * {@code super.clone}.  If a class and all of its superclasses (except
22      * {@code Object}) obey this convention, it will be the case that
23      * {@code x.clone().getClass() == x.getClass()}.
24      * <p>
25      * By convention, the object returned by this method should be independent
26      * of this object (which is being cloned).  To achieve this independence,
27      * it may be necessary to modify one or more fields of the object returned
28      * by {@code super.clone} before returning it.  Typically, this means
29      * copying any mutable objects that comprise the internal "deep structure"
30      * of the object being cloned and replacing the references to these
31      * objects with references to the copies.  If a class contains only
32      * primitive fields or references to immutable objects, then it is usually
33      * the case that no fields in the object returned by {@code super.clone}
34      * need to be modified.
35      * <p>
36      * The method {@code clone} for class {@code Object} performs a
37      * specific cloning operation. First, if the class of this object does
38      * not implement the interface {@code Cloneable}, then a
39      * {@code CloneNotSupportedException} is thrown. Note that all arrays
40      * are considered to implement the interface {@code Cloneable} and that
41      * the return type of the {@code clone} method of an array type {@code T[]}
42      * is {@code T[]} where T is any reference or primitive type.
43      * Otherwise, this method creates a new instance of the class of this
44      * object and initializes all its fields with exactly the contents of
45      * the corresponding fields of this object, as if by assignment; the
46      * contents of the fields are not themselves cloned. Thus, this method
47      * performs a "shallow copy" of this object, not a "deep copy" operation.
48      * <p>
49      * The class {@code Object} does not itself implement the interface
50      * {@code Cloneable}, so calling the {@code clone} method on an object
51      * whose class is {@code Object} will result in throwing an
52      * exception at run time.
53      *
54      * @return     a clone of this instance.
55      * @throws  CloneNotSupportedException  if the object's class does not
56      *               support the {@code Cloneable} interface. Subclasses
57      *               that override the {@code clone} method can also
58      *               throw this exception to indicate that an instance cannot
59      *               be cloned.
60      * @see java.lang.Cloneable
61      */
62     protected native Object clone() throws CloneNotSupportedException;
63 
64 
65 
66 // Cloneable.java
67 
68 package java.lang;
69 
70 /**
71  * A class implements the <code>Cloneable</code> interface to
72  * indicate to the {@link java.lang.Object#clone()} method that it
73  * is legal for that method to make a
74  * field-for-field copy of instances of that class.
75  * <p>
76  * Invoking Object's clone method on an instance that does not implement the
77  * <code>Cloneable</code> interface results in the exception
78  * <code>CloneNotSupportedException</code> being thrown.
79  * <p>
80  * By convention, classes that implement this interface should override
81  * <tt>Object.clone</tt> (which is protected) with a public method.
82  * See {@link java.lang.Object#clone()} for details on overriding this
83  * method.
84  * <p>
85  * Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
86  * Therefore, it is not possible to clone an object merely by virtue of the
87  * fact that it implements this interface.  Even if the clone method is invoked
88  * reflectively, there is no guarantee that it will succeed.
89  *
90  * @author  unascribed
91  * @see     java.lang.CloneNotSupportedException
92  * @see     java.lang.Object#clone()
93  * @since   JDK1.0
94  */
95 public interface Cloneable {
96 }
View Code

2.1 淺拷貝orm

構建一個Student類,該類存在引用類型的屬性Address addr。爲了實現對Student類對象的拷貝(使用clone()方法),對象

須要實現Cloneable接口,而且重寫clone()方法。

 1 package CloneExample;
 2 
 3 public class Student implements Cloneable{
 4     private int id;
 5     private String name;
 6     private Address addr;
 7 
 8     public Student(int id, String name, Address addr) {
 9         this.id = id;
10         this.name = name;
11         this.addr = addr;
12     }
13 
14     public int getId() {
15         return id;
16     }
17 
18     public void setId(int id) {
19         this.id = id;
20     }
21 
22     public String getName() {
23         return name;
24     }
25 
26     public void setName(String name) {
27         this.name = name;
28     }
29 
30     public Address getAddr() {
31         return addr;
32     }
33 
34     public void setAddr(Address addr) {
35         this.addr = addr;
36     }
37 
38     @Override
39     public Object clone(){
40         Student stu = null;
41         try{
42             stu = (Student)super.clone();    // 淺拷貝
43         }catch(CloneNotSupportedException e) {
44             e.printStackTrace();
45         }
46         return stu;
47     }
48 
49     @Override
50     public String toString() {
51         return "Student{" +
52                 "id=" + id +
53                 ", name='" + name + '\'' +
54                 ", addr=" + addr +
55                 '}';
56     }
57 }
58 
59 class Address {
60     private String addr;
61 
62     public Address(String addr) {
63         this.addr = addr;
64     }
65 
66     public String getAddr() {
67         return addr;
68     }
69 
70     public void setAddr(String addr) {
71         this.addr = addr;
72     }
73 
74     @Override
75     public String toString() {
76         return '\'' + addr + '\'';
77     }
78 }

查看結果以下,能夠發現修改name屬性時,兩個對象沒用同步修改;可是修改addr屬性時,兩個對象的addr屬性同步改變。

此時因爲對Student對象的Address addr屬性只是引用傳遞,因此稱對Student對象進行了淺拷貝

 1     public static void main(String[] args){
 2         Address addr = new Address("nanjing");
 3         Student stu1 = new Student(1, "stu1", addr);
 4         Student stu2 = (Student) stu1.clone();                 // 淺拷貝
 5         System.out.println(stu1==stu2);                        // false
 6         System.out.println(stu1.getAddr()==stu2.getAddr());    // true
 7         stu2.setName("stu2");
 8         System.out.println(stu1.toString());    // Student{id=1, name='stu1', addr='nanjing'}
 9         System.out.println(stu2.toString());    // Student{id=1, name='stu2', addr='nanjing'}
10         addr.setAddr("shanghai");
11         System.out.println(stu1.toString());    // Student{id=1, name='stu1', addr='shanghai'}
12         System.out.println(stu2.toString());    // Student{id=1, name='stu2', addr='nanjing'}
13     }

2.2 深拷貝

那麼如何實現對Student對象的深拷貝呢?

1. 對該對象內部的引用數據類型的類實現Cloneable接口和重寫clone()方法。

2. 對該對象進行序列化,以後再進行反序列化。

2.2.1 Address類實現Cloneable接口和重寫clone()方法

 1 class Address implements Cloneable{
 2     private String addr;
 3 
 4     public Address(String addr) {
 5         this.addr = addr;
 6     }
 7 
 8     public String getAddr() {
 9         return addr;
10     }
11 
12     public void setAddr(String addr) {
13         this.addr = addr;
14     }
15 
16     @Override
17     public Object clone() {
18         Address addr = null;
19         try{
20             addr = (Address)super.clone();
21         }catch(CloneNotSupportedException e) {
22             e.printStackTrace();
23         }
24         return addr;
25     }
26 
27     @Override
28     public String toString() {
29         return '\'' + addr + '\'';
30     }
31 }

2.2.2 繼續重寫Student類的clone()方法

 1     @Override
 2     public Object clone(){
 3         Student stu = null;
 4         try{
 5             stu = (Student)super.clone();    // 淺拷貝
 6             stu.addr = (Address) this.addr.clone();   // new add
 7         }catch(CloneNotSupportedException e) {
 8             e.printStackTrace();
 9         }
10         return stu;
11     }

查看結果:

能夠發現,此時修改addr屬性,只有stu1進行了改變,stu2沒有發生變換。

 1     public static void main(String[] args){
 2         Address addr = new Address("nanjing");
 3         Student stu1 = new Student(1, "stu1", addr);
 4         Student stu2 = (Student) stu1.clone();                 // 淺拷貝
 5         System.out.println(stu1==stu2);                        // false
 6         System.out.println(stu1.getAddr()==stu2.getAddr());    // false
 7         stu2.setName("stu2");
 8         System.out.println(stu1.toString());    // Student{id=1, name='stu1', addr='nanjing'}
 9         System.out.println(stu2.toString());    // Student{id=1, name='stu2', addr='nanjing'}
10         addr.setAddr("shanghai");
11         System.out.println(stu1.toString());    // Student{id=1, name='stu1', addr='shanghai'}
12         System.out.println(stu2.toString());    // Student{id=1, name='stu2', addr='nanjing'}
13     }

2.2.3 Student和Address類經過Serializable接口序列化

 1 class Student implements Serializable{
 2     private int id;
 3     private String name;
 4     private Address addr;
 5 
 6     public Student(int id, String name, Address addr) {
 7         this.id = id;
 8         this.name = name;
 9         this.addr = addr;
10     }
11 
12     public void setName(String name) {
13         this.name = name;
14     }
15 
16     public Address getAddr() {
17         return addr;
18     }
19 
20     @Override
21     public String toString() {
22         return "Student{" +
23                 "id=" + id +
24                 ", name='" + name + '\'' +
25                 ", addr=" + addr +
26                 '}';
27     }
28 
29     public Student deepClone() throws IOException, ClassNotFoundException, OptionalDataException
30     {
31         //將對象寫入流中
32         ByteArrayOutputStream bao=new  ByteArrayOutputStream();
33         ObjectOutputStream oos=new ObjectOutputStream(bao);
34         oos.writeObject(this);
35 
36         //將對象從流中取出
37         ByteArrayInputStream bis=new  ByteArrayInputStream(bao.toByteArray());
38         ObjectInputStream ois=new  ObjectInputStream(bis);
39         return  (Student) ois.readObject();
40     }
41 
42 
43 
44 class Address implements Serializable {
45     private String addr;
46 
47     public Address(String addr) {
48         this.addr = addr;
49     }
50 
51     public void setAddr(String addr) {
52         this.addr = addr;
53     }
54 
55     @Override
56     public String toString() {
57         return '\'' + addr + '\'';
58     }
59 }

查看結果:

能夠發現,此時修改addr屬性,只有stu1進行了改變,stu2沒有發生變換。

 1     public static void main(String[] args){
 2         Address addr = new Address("nanjing");
 3         Student stu1 = new Student(1, "stu1", addr);
 4         Student stu2 = (Student) stu1.clone();                 // 深拷貝
 5         System.out.println(stu1==stu2);                        // false
 6         System.out.println(stu1.getAddr()==stu2.getAddr());    // false
 7         stu2.setName("stu2");
 8         System.out.println(stu1.toString());    // Student{id=1, name='stu1', addr='nanjing'}
 9         System.out.println(stu2.toString());    // Student{id=1, name='stu2', addr='nanjing'}
10         addr.setAddr("shanghai");
11         System.out.println(stu1.toString());    // Student{id=1, name='stu1', addr='shanghai'}
12         System.out.println(stu2.toString());    // Student{id=1, name='stu2', addr='nanjing'}
13     }

3. 總結

綜上所述可知,淺拷貝指在拷貝對象時,對於基本數據類型的變量會從新複製一份,而對於引用類型的變量只是對引用進行拷貝,

沒有對引用指向的對象進行拷貝(新建一個對象)。而深拷貝是指在拷貝對象時,同時會對引用指向的對象進行拷貝。

!!!

相關文章
相關標籤/搜索