前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java中复制对象通过反射或序列化

java中复制对象通过反射或序列化

作者头像
Ryan-Miao
发布2018-03-13 12:28:23
1.3K0
发布2018-03-13 12:28:23
举报
文章被收录于专栏:Ryan MiaoRyan Miao

在使用缓存读取数据后修改发现缓存被修改。于是找了下复制对象的方法。

关于对象克隆


按我的理解,对象是包含引用+数据。通常变量复制都是将引用传递过去。比如:

代码语言:javascript
复制
1 Person p1 = new Person();
2 Person p2 = p1;

这两句话,创建两个引用p1,p2,但指向共同的内存大堆数据。修改任何一个,另一个的数据也将修改。

直接引用传递测试用例:

1.实体类:

代码语言:javascript
复制
  1 package com.test.java;
  2 
  3 import java.io.Serializable;
  4 
  5 /**
  6  * Created by Administrator on 2015/9/16.
  7  */
  8 public class Person implements Serializable{
  9     /**
 10      * 
 11      */
 12     private static final long serialVersionUID = 1L;
 13     int id;
 14     String name;
 15     int age;
 16     Country Country;
 17 
 18     
 19     @Override
 20     public String toString() {
 21         return "Person{" +
 22                 "id=" + id +
 23                 ", name='" + name + '\'' +
 24                 ", age=" + age +
 25                 ", Country=" + Country +
 26                 ", hashcode=" + hashCode() +
 27                 '}';
 28     }
 29 
 30     public Person() {
 31     }
 32 
 33     public Person(Country country, int id, String name) {
 34         Country = country;
 35         this.id = id;
 36         this.name = name;
 37     }
 38 
 39     public int getId() {
 40         return id;
 41     }
 42 
 43     public void setId(int id) {
 44         this.id = id;
 45     }
 46 
 47     public String getName() {
 48         return name;
 49     }
 50 
 51     public void setName(String name) {
 52         this.name = name;
 53     }
 54 
 55     public int getAge() {
 56         return age;
 57     }
 58 
 59     public void setAge(int age) {
 60         this.age = age;
 61     }
 62 
 63     public com.test.java.Country getCountry() {
 64         return Country;
 65     }
 66 
 67     public void setCountry(com.test.java.Country Country) {
 68         this.Country = Country;
 69     }
 70 
 71     public Person(String name,int age){
 72 
 73         this.name=name;
 74         this.age=age;
 75     }
 76 }
 77 
 78 class Country implements Serializable{
 79     /**
 80      * 
 81      */
 82     private static final long serialVersionUID = 1L;
 83     int code;
 84     String name;
 85 
 86     public Country() {
 87     }
 88 
 89     public Country(int code) {
 90         this.code = code;
 91     }
 92 
 93     public Country(int code, String name) {
 94         this.code = code;
 95         this.name = name;
 96     }
 97 
 98     public int getCode() {
 99         return code;
100     }
101 
102     public void setCode(int code) {
103         this.code = code;
104     }
105 
106     public String getName() {
107         return name;
108     }
109 
110     public void setName(String name) {
111         this.name = name;
112     }
113 
114     @Override
115     public String toString() {
116         return "Country{" +
117                 "code=" + code +
118                 ", name='" + name + '\'' +
119                 ", hashcode='" + hashCode() + '\'' +
120                 '}';
121     }
122 }

2.测试类

代码语言:javascript
复制
 1 package com.test.java;
 2 
 3 /**
 4  * Created by Administrator on 2015/11/26.
 5  * 测试对象引用
 6  */
 7 public class TestRef {
 8 
 9     public static void main(String[] args) {
10         Country country = new Country(1,"china");
11         Person person = new Person(country,1,"test");
12 
13         Country country1 = country;
14         Person person1 = person;
15 
16         System.out.println("创建国家       :"+country);
17         System.out.println("引用传递国家  :"+country1);
18         System.out.println("创建人         :"+person);
19         System.out.println("引用传递创建人:"+person1);
20 
21     }
22 
23 }

3.打印结果:

4.分析:

通过hashcode可以证明,数据实体的地址是相同的。关于基本类型和引用类型的内存关系,可以参考这篇

同样,通过实现clone接口,重载clone方法,然后调用person.clone()来复制对象的浅克隆是一样。参考这篇

当然,采用深度克隆的话就可以生成两个完全不同的对象。

然而,我们创建的实体通常是不会实现和覆盖clone的,这种办法只能提前写好对应的类才可以实现。因此,不推荐使用。

那么,我们可以通过反射或者序列化来实现。


关于序列化


参考博客Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。字节码可以存储,无状态,而对象在内存中开辟空间,有地址。

由此,可以把对象序列化后反序列化。相当于破碎重组。

前提是:实体类需要实现序列化接口

1.序列化实现对象复制

代码语言:javascript
复制
 1 // 用序列化与反序列化实现深克隆
 2     public static Object cloneBySer(Object baseObj) {
 3         Object o = null;
 4         try {
 5             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 6             ObjectOutputStream oos = new ObjectOutputStream(baos);
 7             oos.writeObject(baseObj);
 8             oos.close();
 9             ByteArrayInputStream bais = new ByteArrayInputStream(baos
10                     .toByteArray());
11             ObjectInputStream ois = new ObjectInputStream(bais);
12             o = ois.readObject();
13             ois.close();
14         } catch (IOException e) {
15             e.printStackTrace();
16         } catch (ClassNotFoundException e) {
17             e.printStackTrace();
18         }
19         return o;
20     }

2.测试用例:

代码语言:javascript
复制
 1  public static void main(String[] args) {
 2         Country country = new Country(1,"china");
 3         Person person = new Person(country,1,"test");
 4         //引用传递
 5         Country country1 = country;
 6         Person person1 = person;
 7         //序列化和反序列化
 8         Object person2 = ObjectCopy.cloneBySer(person);
 9         Object country2 = ObjectCopy.cloneBySer(country);
10         
11 //        person.setAge(12);
12         
13         System.out.println("创建国家       :"+country);
14         System.out.println("引用传递国家  :"+country1);
15         System.out.println("序列化复制国家  :"+country2);
16         System.out.println("创建人         :"+person);
17         System.out.println("引用传递人:"+person1);
18         System.out.println("序列化复制人:"+person2);
19 
20     }

3.控制台打印:

4.分析

序列化完全实现了对象拷贝。要求:对象都实现序列化,对象hashcode和equals方法默认或者包含全部信息。


通过反射


反射可以复制一个对象的属性,从而实现对象拷贝

反射代码:

代码语言:javascript
复制
 1 /**
 2      * COPY对象(毛病还是很多的。。)
 3      *对基本类型的过滤
 4      * @author Lv9
 5      * @since 2010.03.09
 6      * baseObject 要拷贝的对象
 7      * noCopyClassNames 不深度拷贝的对象属性
 8      */
 9     public static Object coloneByRef(Object baseObject,
10                                     String... noCopyClassNames) throws Exception {
11         Object copyObject = baseObject.getClass().newInstance();
12         Field[] fields = baseObject.getClass().getDeclaredFields();
13         for (Field field : fields) {
14             field.setAccessible(true);
15             if (checkClassType(field.getType().getName(), noCopyClassNames)) {
16                 field.set(copyObject, field.get(baseObject));
17             } else {
18                 field.set(copyObject, coloneByRef(field.get(baseObject),
19                         noCopyClassNames));
20             }
21         }
22         return copyObject;
23     }
24 
25     public static boolean checkClassType(String className,
26                                          String[] noCopyClassNames) {
27         for (String noCopyClassName : noCopyClassNames) {
28             if (className.equals(noCopyClassName)) {
29                 return true;
30             }
31         }
32         return false;
33     }

一个失败的用例:

反射用的不太会

代码语言:javascript
复制
  1 package com.test.reflect;
  2 
  3 import java.io.*;
  4 import java.lang.reflect.Field;
  5 
  6 /**
  7  * Created by Administrator on 2015/11/25.
  8  * 对象复制
  9  */
 10 public class ObjectCopy {
 11 
 12     public static void main(String[] args) throws Exception {
 13         A baseObject = new A(new B(new C("bString1", "bString2"), 1, 2), new C(
 14                 "cString1", "cString2"));
 15         A copyObject = (A) coloneByRef(baseObject, "java.lang.Integer",
 16                 "java.lang.String");
 17 
 18         System.out.println(baseObject);
 19         System.out.println(copyObject);
 20         System.out.println("===================分割线===================");
 21         System.out.println("原对象修改前:"+baseObject);
 22         A a = (A)cloneBySer(baseObject);
 23         System.out.println("复制对象     :"+a);
 24         a.setC(new C("cchange1","cchange2"));
 25         System.out.println("复制后修改对象:"+a);
 26         System.out.println("原对象修改后  :"+baseObject);
 27 
 28     }
 29 
 30     /**
 31      * COPY对象(毛病还是很多的。。)
 32      *对基本类型的过滤
 33      * @author Lv9
 34      * @since 2010.03.09
 35      * baseObject 要拷贝的对象
 36      * noCopyClassNames 不深度拷贝的对象属性
 37      */
 38     public static Object coloneByRef(Object baseObject,
 39                                     String... noCopyClassNames) throws Exception {
 40         Object copyObject = baseObject.getClass().newInstance();
 41         Field[] fields = baseObject.getClass().getDeclaredFields();
 42         for (Field field : fields) {
 43             field.setAccessible(true);
 44             if (checkClassType(field.getType().getName(), noCopyClassNames)) {
 45                 field.set(copyObject, field.get(baseObject));
 46             } else {
 47                 field.set(copyObject, coloneByRef(field.get(baseObject),
 48                         noCopyClassNames));
 49             }
 50         }
 51         return copyObject;
 52     }
 53 
 54     public static boolean checkClassType(String className,
 55                                          String[] noCopyClassNames) {
 56         for (String noCopyClassName : noCopyClassNames) {
 57             if (className.equals(noCopyClassName)) {
 58                 return true;
 59             }
 60         }
 61         return false;
 62     }
 63 
 64     // 用序列化与反序列化实现深克隆
 65     public static Object cloneBySer(Object baseObj) {
 66         Object o = null;
 67         try {
 68             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 69             ObjectOutputStream oos = new ObjectOutputStream(baos);
 70             oos.writeObject(baseObj);
 71             oos.close();
 72             ByteArrayInputStream bais = new ByteArrayInputStream(baos
 73                     .toByteArray());
 74             ObjectInputStream ois = new ObjectInputStream(bais);
 75             o = ois.readObject();
 76             ois.close();
 77         } catch (IOException e) {
 78             e.printStackTrace();
 79         } catch (ClassNotFoundException e) {
 80             e.printStackTrace();
 81         }
 82         return o;
 83     }
 84 }
 85 
 86 class A implements Serializable{
 87     /**
 88      * 
 89      */
 90     private B b;
 91     private C c;
 92 
 93     public A() {
 94 
 95     }
 96 
 97     public A(B b, C c) {
 98         this.b = b;
 99         this.c = c;
100     }
101 
102     public B getB() {
103         return b;
104     }
105 
106     public void setB(B b) {
107         this.b = b;
108     }
109 
110     public C getC() {
111         return c;
112     }
113 
114     public void setC(C c) {
115         this.c = c;
116     }
117 
118 
119     @Override
120     public String toString() {
121         return "A{" +
122                 " b=" + b +
123                 ", c=" + c +
124                 '}';
125     }
126 }
127 
128 class B implements Serializable{
129     /**
130      * 
131      */
132     private C c;
133     private Integer int1;
134     private Integer int2;
135 
136     public B() {
137 
138     }
139 
140     public B(C c, Integer int1, Integer int2) {
141         this.c = c;
142         this.int1 = int1;
143         this.int2 = int2;
144     }
145 
146     public C getC() {
147         return c;
148     }
149 
150     public void setC(C c) {
151         this.c = c;
152     }
153 
154     public Integer getInt1() {
155         return int1;
156     }
157 
158     public void setInt1(Integer int1) {
159         this.int1 = int1;
160     }
161 
162     public Integer getInt2() {
163         return int2;
164     }
165 
166     public void setInt2(Integer int2) {
167         this.int2 = int2;
168     }
169 
170     @Override
171     public String toString() {
172         return "[B: int1 = " + int1 + ",int2 = " + int2 + ",c = " + c
173                 + ",hashCode = " + hashCode() + "]";
174     }
175 }
176 
177 class C implements Serializable{
178     /**
179      * 
180      */
181     private String string1;
182     private String string2;
183 
184     public C() {
185 
186     }
187 
188     public C(String string1, String string2) {
189         this.string1 = string1;
190         this.string2 = string2;
191     }
192 
193     public String getString1() {
194         return string1;
195     }
196 
197     public void setString1(String string1) {
198         this.string1 = string1;
199     }
200 
201     public String getString2() {
202         return string2;
203     }
204 
205     public void setString2(String string2) {
206         this.string2 = string2;
207     }
208 
209     @Override
210     public String toString() {
211         return "[C: string1 = " + string1 + ",string2 = " + string2
212                 + ",hashCode = " + hashCode() + "]";
213     }
214 }

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 关于对象克隆
    • 直接引用传递测试用例:
    • 关于序列化
      • 1.序列化实现对象复制
      • 通过反射
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档