在使用缓存读取数据后修改发现缓存被修改。于是找了下复制对象的方法。
关于对象克隆
按我的理解,对象是包含引用+数据。通常变量复制都是将引用传递过去。比如:
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 }
唯有不断学习方能改变! -- Ryan Miao