首页 > 代码库 > JAVA 对象拷贝

JAVA 对象拷贝

JAVA 对象拷贝

为什么需要有对象拷贝?

对象拷贝相对的自然是引用拷贝。java初学者经常会问,我这个方法要改变一个对象的属性,可以把参数传进去了,为什么没有改变了?

——基本数据类型传值,而对象传引用或引用的拷贝。

而有时候我们要获取到一个当前状态的对象复制品,他们是两个独立对象。不再是引用或者引用拷贝(实质都是指向对象本身)。就是说a是b的拷贝,b发生变化的时候,不要影响a。


对象拷贝有浅拷贝和深度拷贝两种。

1)浅拷贝

浅拷贝是指对象中基本数据类型得到拷贝,而引用数据类型并未拷贝。
提到拷贝自然和clone联系起来了,所有具有clone功能的类都有一个特性,那就是它直接或间接地实现了Cloneable接口。
否则,我们在尝试调用clone()方法时,将会触发CloneNotSupportedException异常。
eg:

 1  public   class  DOG  implements  Cloneable
 2  {
 3      public  DOG(String name,  int  age)
 4       {
 5          this .name  =  name;
 6          this .age  =  age;
 7     } 
 8  
 9      public  String getName()
10       {
11          return   this .name;
12     } 
13  
14      public   int  getAge()
15       {
16          return   this .age;
17     } 
18  
19      public  Object clone()
20       {
21          try 
22           {
23              return   super .clone();
24  
25         }   catch  (CloneNotSupportedException e)
26           {
27              return   null ;
28         } 
29     } 
30  
31      public  String name;
32  
33      private   int  age;
34  
35      // test 
36       public   static   void  main(String[] args)
37       {
38         DOG dog1  =   new  DOG( " xiaogou " ,  2 );
39         DOG dog2  =  (DOG) dog1.clone();
40         dog1.name  =   " dagou " ;
41         System.out.println(dog2.getName());
42         System.out.println(dog2.getAge());
43         System.out.println(dog1.getName());
44         System.out.println(dog1.getAge());
45  
46     } 
47  
48 
49 



运行结果:

xiaogou
2
dagou
2

2)深度拷贝

相对浅拷贝。实现对象中基本数据类型和引用数据类型的拷贝。

请先看下面代码:

 

 1  class  AAA
 2  {
 3      public  AAA(String name)
 4       {
 5          this .name  =  name;
 6     } 
 7  
 8      public  String name;
 9 
10  
11  class  DOG  implements  Cloneable
12  {
13      public  DOG(String name,  int  age, AAA birthday)
14       {
15          this .name  =  name;
16          this .age  =  age;
17          this .birthday  =  birthday;
18     } 
19  
20      public  String getName()
21       {
22          return  name;
23     } 
24  
25      public   int  getAge()
26       {
27          return  age;
28     } 
29  
30      public  AAA getBirthday()
31       {
32          return  birthday;
33     } 
34  
35      public  String getBirth(AAA a)
36       {
37          return  a.name;
38     } 
39  
40      public  String name;
41  
42      private   int  age;
43  
44      public  AAA birthday;
45  
46      public  Object clone()
47       {
48          try 
49           {
50              super .clone();
51              return   super .clone();
52         }   catch  (Exception e)
53           {
54              return   null ;
55         } 
56     } 
57 
58  
59  public   class  TestClone
60  {
61      public   static   void  main(String[] args)
62       {
63         AAA Day  =   new  AAA( " test " );
64         DOG dog1  =   new  DOG( " xiaogou " ,  2 , Day);
65         DOG dog2  =  (DOG) dog1.clone();
66          //   dog2.birthday = (AAA) dog1.birthday.clone();  
67          dog1.birthday.name  =   " 333 " ;
68         System.out.println(dog1.getBirth(dog1.birthday));
69         System.out.println(dog2.getBirth(dog2.birthday));
70     } 
71 
72 


运行结果是:
333
333
而真正要实现拷贝还的加点代码,如下请对比上面和下面代码的异同之处:

 1  class  AAA  implements  Cloneable
 2  {
 3      public  AAA(String name)
 4       {
 5          this .name  =  name;
 6     } 
 7  
 8      public  Object clone()
 9       {
10          try 
11           {
12              super .clone();
13              return   super .clone();
14         }   catch  (Exception e)
15           {
16              return   null ;
17         } 
18     } 
19  
20      public  String name;
21 
22  
23  class  DOG  implements  Cloneable
24  {
25      public  DOG(String name,  int  age, AAA birthday)
26       {
27          this .name  =  name;
28          this .age  =  age;
29          this .birthday  =  birthday;
30     } 
31  
32      public  String getName()
33       {
34          return  name;
35     } 
36  
37      public   int  getAge()
38       {
39          return  age;
40     } 
41  
42      public  AAA getBirthday()
43       {
44          return  birthday;
45     } 
46  
47      public  String getBirth(AAA a)
48       {
49          return  a.name;
50     } 
51  
52      public  String name;
53  
54      private   int  age;
55  
56      public  AAA birthday;
57  
58      public  Object clone()
59       {
60          try 
61           {
62              super .clone();
63              return   super .clone();
64         }   catch  (Exception e)
65           {
66              return   null ;
67         } 
68     } 
69 
70  
71  public   class  TestClone
72  {
73      public   static   void  main(String[] args)
74       {
75         AAA Day  =   new  AAA( " test " );
76         DOG dog1  =   new  DOG( " xiaogou " ,  2 , Day);
77         DOG dog2  =  (DOG) dog1.clone();
78         dog2.birthday  =  (AAA) dog1.birthday.clone(); // 特别注意这里 
79          dog1.birthday.name  =   " 333 " ;
80         System.out.println(dog1.getBirth(dog1.birthday));
81         System.out.println(dog2.getBirth(dog2.birthday));
82     } 
83 
84 


运行结果:
333
test
这样基本就达到了我们当初的母的。


但是明显的这种方法还是有许多不足,人们总是希望一个clone就是对象直接克隆。而上面还要对对象中的对象递归使用clone。下面提供一种更高级点的做法:

 

 1  import  java.io. * ;
 2  
 3  class  AAA  implements  Serializable
 4  {
 5      public  AAA(String name)
 6       {
 7          this .name  =  name;
 8     } 
 9  
10      public  String name;
11 
12  
13  class  DOG  extends  SerialCloneable
14  {
15      public  DOG(String name,  int  age, AAA birthday)
16       {
17          this .name  =  name;
18          this .age  =  age;
19          this .birthday  =  birthday;
20     } 
21  
22      public  String getName()
23       {
24          return  name;
25     } 
26  
27      public   int  getAge()
28       {
29          return  age;
30     } 
31  
32      public  AAA getBirthday()
33       {
34          return  birthday;
35     } 
36  
37      public  String getBirth(AAA a)
38       {
39          return  a.name;
40     } 
41  
42      public  String name;
43  
44      private   int  age;
45  
46      public  AAA birthday;
47  
48      public  Object clone()
49       {
50          try 
51           {
52              super .clone();
53              return   super .clone();
54         }   catch  (Exception e)
55           {
56              return   null ;
57         } 
58     } 
59 
60  
61  public   class  TestClone
62  {
63      public   static   void  main(String[] args)
64       {
65         AAA Day  =   new  AAA( " test " );
66         DOG dog1  =   new  DOG( " xiaogou " ,  2 , Day);
67         DOG dog2  =  (DOG) dog1.clone();
68          // dog2.birthday = (AAA) dog1.birthday.clone(); 
69          dog1.birthday.name  =   " 333 " ;
70         System.out.println(dog1.getBirth(dog1.birthday));
71         System.out.println(dog2.getBirth(dog2.birthday));
72     } 
73 
74  
75  class  SerialCloneable  implements  Cloneable, Serializable
76  {
77      public  Object clone()
78       {
79          try 
80           {
81             ByteArrayOutputStream bout  =   new  ByteArrayOutputStream();
82             ObjectOutputStream out  =   new  ObjectOutputStream(bout);
83             out.writeObject( this );
84             out.close();
85             ByteArrayInputStream bin  =   new  ByteArrayInputStream(bout
86                     .toByteArray());
87             ObjectInputStream in  =   new  ObjectInputStream(bin);
88             Object ret  =  in.readObject();
89             in.close();
90              return  ret;
91         }   catch  (Exception e)
92           {
93              return   null ;
94         } 
95     } 
96 
97 


输出:
333
test

上面的代码用序列化与反序列化实现了对象拷贝。比较通用。但是得注意的是其中的类得implements Serializable。

 

3)后记

我们如果利用强大的反射机制+序列化与反序列化,能做出更加灵活的对象拷贝。有兴趣的朋友可以自行去研究。
我在javaeye上看到一篇短文:http://www.javaeye.com/post/367014 主要讲的就是反射在对象拷贝中的应用。

 

JAVA 对象拷贝