在使用缓存读取数据后修改发现缓存被修改。于是找了下复制对象的方法。
按我的理解,对象是包含引用+数据。通常变量复制都是将引用传递过去。比如:
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 // 用序列化与反序列化实现深克隆
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 }