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

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

关于对象克隆


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

1 Person p1 = new Person();
2 Person p2 = p1;

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

直接引用传递测试用例:

1.实体类:

  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.测试类

 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.序列化实现对象复制

 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.测试用例:

 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方法默认或者包含全部信息。


通过反射


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

反射代码:

 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     }

一个失败的用例:

反射用的不太会

  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 }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏吴小龙同學

Kotlin for Android(三)类和对象

久等了,关于 Kotlin 的类和对象涉及的知识有点多,我又顺便练手写了 Kotlin 版 Retrofit +RxJava,近期整理一并分享出来。 类 ...

3096
来自专栏一枝花算不算浪漫

[读书笔记]C#学习笔记二: 委托和事件的用法及不同.

35615
来自专栏个人随笔

析构函数(C#)

 析构函数又称终结器,用于析构类的实例。 定义   析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系...

3097
来自专栏技术总结

《Objective-C高级编程》温故知新之"Blocks"

在计算机科学中,此概念也称为闭包(Closure)、lambda计算等。Swift中称作闭包

1214
来自专栏owent

VC和GCC成员函数指针实现的研究(二)

调用的时候主要看(c.*vptr2)()的代码。因为(c.vptr1)()生成的和单继承一样。而由于它们最终都转向vcall,所以vptr2的时候调整了虚表指针...

702
来自专栏IT大咖说

深入学习 Java 序列化

1834
来自专栏恰同学骚年

《C#图解教程》读书笔记之六:接口和转换

  假设有如下一段代码,它使用Array类的一个静态方法Sort对一个未排序的int类型数组进行排序,并输出排序后的结果。

783
来自专栏C/C++基础

动态联编实现原理分析

所谓动态联编,是指被调函数入口地址是在运行时、而不是在编译时决定的。C++语言利用动态联编来完成虚函数调用。C++标准并没有规定如何实现动态联编,但大多数的C+...

711
来自专栏雨尘分享

3. __block  __weak  __strong   这都是做什么的

1723
来自专栏博客园

.NET面试题解析(03)-string与字符串操作

4.以下代码执行后内存中会存在多少个字符串?分别是什么?输出结果是什么?为什么呢?

532

扫码关注云+社区