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 主要讲的就是反射在对象拷贝中的应用。


 

posted on 2008-04-28 11:52 -274°C 阅读(10228) 评论(6)  编辑  收藏 所属分类: JAVA


FeedBack:
# re: JAVA 对象拷贝
2008-04-28 12:51 | Jacky-Q
这代码缩进真难受....  回复  更多评论
  
# re: JAVA 对象拷贝
2008-04-28 14:40 | 々上善若水々
风中叶的吧。  回复  更多评论
  
# re: JAVA 对象拷贝
2008-04-29 10:47 | cc
super .clone();
return super .clone();
这里为什么要写两次呢,直接写
return super .clone();
不是更好吗?  回复  更多评论
  
# re: JAVA 对象拷贝
2008-04-29 12:44 | java-he
@ CC
同意。  回复  更多评论
  
# re: JAVA 对象拷贝[未登录]
2008-04-30 13:56 | java
不错~  回复  更多评论
  
# re: JAVA 对象拷贝
2012-07-02 11:25 | j2
楼主连什么是递归都没有搞清楚,错别字,错概念太多了  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
 

常用链接

留言簿(21)

随笔分类(265)

随笔档案(242)

相册

JAVA网站

关注的Blog

搜索

  •  

积分与排名

  • 积分 - 872894
  • 排名 - 37

最新评论