深克隆有两种实现方式:
1)基于浅克隆的基础上通过覆盖Object类的clone()方法实现。
为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:
1 package abc; 2 3 class Address implements Cloneable { 4 private String add; 5 6 public String getAdd() { 7 return add; 8 } 9 10 public void setAdd(String add) { 11 this.add = add; 12 } 13 14 @Override 15 public Object clone() { 16 Address addr = null; 17 try{ 18 addr = (Address)super.clone(); 19 }catch(CloneNotSupportedException e) { 20 e.printStackTrace(); 21 } 22 return addr; 23 } 24 } 25 26 class Student implements Cloneable{ 27 private int number; 28 29 private Address addr; 30 31 public Address getAddr() { 32 return addr; 33 } 34 35 public void setAddr(Address addr) { 36 this.addr = addr; 37 } 38 39 public int getNumber() { 40 return number; 41 } 42 43 public void setNumber(int number) { 44 this.number = number; 45 } 46 47 @Override 48 public Object clone() { 49 Student stu = null; 50 try{ 51 stu = (Student)super.clone(); //浅复制 52 }catch(CloneNotSupportedException e) { 53 e.printStackTrace(); 54 } 55 stu.addr = (Address)addr.clone(); //深度复制 56 return stu; 57 } 58 } 59 public class Test { 60 61 public static void main(String args[]) { 62 63 Address addr = new Address(); 64 addr.setAdd("杭州市"); 65 Student stu1 = new Student(); 66 stu1.setNumber(123); 67 stu1.setAddr(addr); 68 69 Student stu2 = (Student)stu1.clone(); 70 71 System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd()); 72 System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd()); 73 74 addr.setAdd("西湖区"); 75 76 System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd()); 77 System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd()); 78 } 79 }
结果:
学生1:123,地址:杭州市 学生2:123,地址:杭州市 学生1:123,地址:西湖区 学生2:123,地址:杭州市
2)通过序列化(Serialization)的方式(可解决多层克隆的问题)。
如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。
序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。
Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等
public class Student implements Serializable{ private String name; public Address address; //Discription:[深度复制方法,需要对象及对象所有的对象属性都实现序列化] public Student myclone() { Student stu = null; try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); // 将流序列化成对象 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); stu = (Student) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return stu; } }
Address也必须实现Serializable,否则无法序列化
public class Address implements Serializable{ private String add;public Address(String add) { this.add= add; } }
将不需要序列化的属性前添加关键字 transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
被修饰static的属性也不可以序列化 ,序列化保存的是对象的状态,静态变量属于类的状态,因此序列化并不保存静态变量。
总结
实现对象克隆有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
注意 基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。 |
来源:https://www.cnblogs.com/kise-ryota/p/11201479.html