浅拷贝与深拷贝

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. 总结

综上所述可知,浅拷贝指在拷贝对象时,对于基本数据类型的变量会从新复制一份,而对于引用类型的变量只是对引用进行拷贝,

没有对引用指向的对象进行拷贝(新建一个对象)。而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝。

!!!

相关文章
相关标签/搜索