• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
火磷
Memory will fade,but not notes.
博客园    首页    新随笔    联系   管理    订阅  订阅
浅拷贝与深拷贝

1. 简介

在java中除了基本的数据类型外, 还存在类实例对象的引用数据类型。在使用“=”作赋值操作时,对于基本数据类型,拷贝的是它的值;

对于引用数据类型,拷贝的是对这个对象的引用,拷贝对象与原对象仍然指向了同一对象。浅拷贝与深拷贝是在上述基础上进行区分。

在拷贝对象的过程中,如果对其基本数据类型进行拷贝,但对其引用数据类型只进行了引用传递(即没有创建新的对象),则认为该拷

贝是浅拷贝(shallow copy);在拷贝对象的过程中,如果对其基本数据类型拷贝,且在拷贝其引用数据类型时创建了新对象,则认

为该拷贝是深拷贝(deep copy)。

2. 示例

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

 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 浅拷贝

构建一个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. 总结

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

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

!!!

 

posted on 2019-09-03 19:45  火磷  阅读(262)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3