浅克隆和深克隆
有两种方式:
1.实现Cloneable接口并重写Object类中的clone()方法;
2.实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正意义上的深度克隆,代码如下:
1 package com.lovo; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.ObjectInputStream; 6 import java.io.ObjectOutputStream; 7 8 public class MyUtil { 9 10 private MyUtil() { 11 throw new AssertionError(); 12 } 13 14 public static <T> T clone(T obj) throws Exception { 15 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 16 ObjectOutputStream oos = new ObjectOutputStream(bout); 17 oos.writeObject(obj); 18 19 ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); 20 ObjectInputStream ois = new ObjectInputStream(bin); 21 return (T) ois.readObject(); 22 23 // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义 24 // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源 25 } 26 }
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。
1 package com.lovo; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.ObjectInputStream; 6 import java.io.ObjectOutputStream; 7 8 public class MyUtil { 9 10 private MyUtil() { 11 throw new AssertionError(); 12 } 13 14 public static <T> T clone(T obj) throws Exception { 15 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 16 ObjectOutputStream oos = new ObjectOutputStream(bout); 17 oos.writeObject(obj); 18 19 ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); 20 ObjectInputStream ois = new ObjectInputStream(bin); 21 return (T) ois.readObject(); 22 23 // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义 24 // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源 25 } 26 }
假克隆:
通过赋值符号 = 对对象赋值实现;
实现浅克隆:
当需要克隆对象成员变量是基本类型,使用浅克隆可以实现;如果成员变量包括可变的引用类型,则需要深度克隆;
PS:如果引用类型是String等不可变的类型,则不必深度克隆;
需要克隆对象时候,需要调用clone() 方法;该方法的申明:
Protected Object clone() throws CloneNotSupportedException
需要注意是该方法是保护类型的,需要继承Cloneable接口,重写该方法并且修改访问权限为public;
如果类中包含引用类型域,浅克隆就会出现CloneSupportedException 异常;
浅克隆事例:
1 package com.JunitTest.www; 2 3 4 public class Address { 5 private String state; 6 private String province; 7 private String city; 8 9 public Address(String state, String province, String city) { 10 super(); 11 this.state = state; 12 this.province = province; 13 this.city = city; 14 } 15 16 public String getState() { 17 return state; 18 } 19 20 public void setState(String state) { 21 this.state = state; 22 } 23 24 public String getProvince() { 25 return province; 26 } 27 28 public void setProvince(String province) { 29 this.province = province; 30 } 31 32 public String getCity() { 33 return city; 34 } 35 36 public void setCity(String city) { 37 this.city = city; 38 } 39 40 @Override 41 public String toString() { 42 StringBuilder sBuilder = new StringBuilder(); 43 sBuilder.append("国家:" + state + ","); 44 sBuilder.append("省:" + province + ","); 45 sBuilder.append("市:" + city + ","); 46 return sBuilder.toString(); 47 } 48 49 }
1 package com.JunitTest.www; 2 3 public class Employee implements Cloneable { 4 private String name; 5 private int age; 6 private Address address; 7 8 public Employee(String name, int age, Address address) { 9 super(); 10 this.name = name; 11 this.age = age; 12 this.address = address; 13 } 14 15 public String getName() { 16 return name; 17 } 18 19 public void setName(String name) { 20 this.name = name; 21 } 22 23 public int getAge() { 24 return age; 25 } 26 27 public void setAge(int age) { 28 this.age = age; 29 } 30 31 public Address getAddress() { 32 return address; 33 } 34 35 public void setAddress(Address address) { 36 this.address = address; 37 } 38 39 @Override 40 public String toString() { 41 return "Employee [name=" + name + ", age=" + age + ", address=" + address + "]"; 42 } 43 44 @Override 45 protected Employee clone() throws CloneNotSupportedException { 46 Employee employee = null; 47 try { 48 employee = (Employee) super.clone(); 49 } catch (CloneNotSupportedException exception) { 50 exception.printStackTrace(); 51 } 52 return employee; 53 } 54 }
测试代码:
1 package com.JunitTest.www; 2 3 public class Test001 { 4 public static void main(String[] args) throws CloneNotSupportedException { 5 System.out.println("克隆之前:"); 6 Address address = new Address("中国", "四川", "成都"); 7 Employee employee1 = new Employee("张三丰", 12, address); 8 System.out.println("员工1的信息"); 9 System.out.println(employee1); 10 System.out.println("克隆之后"); 11 Employee employee2 = employee1.clone(); 12 employee2.getAddress().setState("中国"); 13 employee2.getAddress().setProvince("四川"); 14 employee2.getAddress().setCity("成都"); 15 employee2.setName("屈耕"); 16 employee2.setAge(24); 17 System.out.println("员工1 的信息"); 18 System.out.println(employee1); 19 System.out.println("员工2的信息"); 20 System.out.println(employee2); 21 } 22 }
输出信息:
克隆之前: 员工1的信息 Employee [name=张三丰, age=12, address=国家:中国,省:四川,市:成都,] 克隆之后 员工1 的信息 Employee [name=张三丰, age=12, address=国家:中国,省:四川,市:成都,] 员工2的信息 Employee [name=屈耕, age=24, address=国家:中国,省:四川,市:成都,]
深度克隆:
1 package com.deepClone.www; 2 3 /** 4 * Ps:因为Address类的域不是基本变量就是不可变类型 ,所以直接使用浅克隆即可 5 * 6 * @author Administrator 7 * 8 */ 9 public class Address implements Cloneable { 10 private String state; // 员工所在国家 11 private String province; // 员工所在省份 12 private String city; // 员工所在城市 13 14 /** 15 * 构造方法初始化 16 * 17 * @param state 18 * @param province 19 * @param city 20 */ 21 public Address(String state, String province, String city) { 22 super(); 23 this.state = state; 24 this.province = province; 25 this.city = city; 26 } 27 28 public String getState() { 29 return state; 30 } 31 32 public void setState(String state) { 33 this.state = state; 34 } 35 36 public String getProvince() { 37 return province; 38 } 39 40 public void setProvince(String province) { 41 this.province = province; 42 } 43 44 public String getCity() { 45 return city; 46 } 47 48 public void setCity(String city) { 49 this.city = city; 50 } 51 52 /** 53 * 实现浅克隆 54 * 55 * @return 56 * @throws CloneNotSupportedException 57 */ 58 @Override 59 protected Object clone() throws CloneNotSupportedException { 60 Address address = null; 61 try { 62 address = (Address) super.clone(); 63 } catch (Exception e) { 64 e.printStackTrace(); 65 } 66 return address; 67 } 68 69 /** 70 * 重写toString()方法 71 * 72 * @return 73 */ 74 @Override 75 public String toString() { 76 return "Address [国家=" + state + ", 省=" + province + ", 市=" + city + "]"; 77 } 78 79 }
新建Employee类并且实现Cloneable接口:
1 package com.deepClone.www; 2 3 public class Employee implements Cloneable { 4 private String name;// 员工姓名 5 private int age;// 员工年龄 6 private Address address;// 员工地址 7 8 // 构造函数实现初始化 9 public Employee(String name, int age, Address address) { 10 super(); 11 this.name = name; 12 this.age = age; 13 this.address = address; 14 } 15 16 public String getName() { 17 return name; 18 } 19 20 public void setName(String name) { 21 this.name = name; 22 } 23 24 public int getAge() { 25 return age; 26 } 27 28 public void setAge(int age) { 29 this.age = age; 30 } 31 32 public Address getAddress() { 33 return address; 34 } 35 36 public void setAddress(Address address) { 37 this.address = address; 38 } 39 40 // 重写Clone() 实现深度克隆 41 @Override 42 protected Object clone() throws CloneNotSupportedException { 43 Employee employee = null; 44 try { 45 employee = (Employee) super.clone(); 46 employee.address = (Address) address.clone(); 47 } catch (Exception e) { 48 e.printStackTrace(); 49 } 50 return employee; 51 } 52 53 // 重写toString() 54 @Override 55 public String toString() { 56 return "Employee [姓名=" + name + ", 年龄=" + age + ", 地址=" + address + "]"; 57 } 58 59 }
测试代码:
1 package com.deepClone.www; 2 3 public class Test001 { 4 public static void main(String[] args) throws CloneNotSupportedException { 5 System.out.println("克隆之前"); 6 Address address = new Address("中国", "四川", "广安"); 7 Employee employee = new Employee("夜雨梧桐", 25, address); 8 System.out.println("员工1的信息"); 9 System.out.println(employee); 10 System.out.println("克隆之后"); 11 Employee employee2 = (Employee) employee.clone(); 12 employee2.getAddress().setState("中国"); 13 employee2.getAddress().setProvince("四川"); 14 employee2.getAddress().setCity("成都"); 15 employee2.setName("强壮的男人"); 16 employee2.setAge(26); 17 employee2.setAddress(address); 18 System.out.println("员工1的信息"); 19 System.out.println(employee); 20 System.out.println("员工2的信息"); 21 System.out.println(employee2); 22 } 23 }
显示效果:
1 克隆之前 2 员工1的信息 3 Employee [姓名=夜雨梧桐, 年龄=25, 地址=Address [国家=中国, 省=四川, 市=广安]] 4 克隆之后 5 员工1的信息 6 Employee [姓名=夜雨梧桐, 年龄=25, 地址=Address [国家=中国, 省=四川, 市=广安]] 7 员工2的信息 8 Employee [姓名=强壮的男人, 年龄=26, 地址=Address [国家=中国, 省=四川, 市=广安]]
但是问题来了,如果对象中的引用类型特别多,需要克隆Clone()就会相当复杂;也可以考虑序列化的方式实现克隆;
新建Address类并且实现Serializable接口:
1 package com.CloneSerializeble.www; 2 3 import java.io.Serializable; 4 5 public class Address implements Serializable { 6 7 /** 8 * 9 */ 10 private static final long serialVersionUID = -5531527688287881990L; 11 private String State; 12 private String Province; 13 private String City; 14 15 public Address(String state, String province, String city) { 16 super(); 17 State = state; 18 Province = province; 19 City = city; 20 } 21 22 public String getState() { 23 return State; 24 } 25 26 public void setState(String state) { 27 State = state; 28 } 29 30 public String getProvince() { 31 return Province; 32 } 33 34 public void setProvince(String province) { 35 Province = province; 36 } 37 38 public String getCity() { 39 return City; 40 } 41 42 public void setCity(String city) { 43 City = city; 44 } 45 46 public static long getSerialversionuid() { 47 return serialVersionUID; 48 } 49 50 @Override 51 public String toString() { 52 53 return "Address [State=" + State + ", Province=" + Province + ", City=" + City + "]"; 54 } 55 56 }
实现Employee 实现Serializable接口:
1 package com.CloneSerializeble.www; 2 3 import java.io.Serializable; 4 5 public class Employee implements Serializable { 6 7 /** 8 * 9 */ 10 private static final long serialVersionUID = -3783147174978411726L; 11 private String name; 12 private int age; 13 private Address address; 14 15 public Employee(String name, int age, Address address) { 16 super(); 17 this.name = name; 18 this.age = age; 19 this.address = address; 20 } 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public int getAge() { 31 return age; 32 } 33 34 public void setAge(int age) { 35 this.age = age; 36 } 37 38 public Address getAddress() { 39 return address; 40 } 41 42 public void setAddress(Address address) { 43 this.address = address; 44 } 45 46 public static long getSerialversionuid() { 47 return serialVersionUID; 48 } 49 50 @Override 51 public String toString() { 52 return "Employee [name=" + name + ", age=" + age + ", address=" + address + "]"; 53 } 54 55 }
测试代码:
1 package com.CloneSerializeble.www; 2 3 import java.io.FileInputStream; 4 import java.io.FileNotFoundException; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.ObjectInputStream; 8 import java.io.ObjectOutputStream; 9 10 /** 11 * 测试代码 12 * 13 * @author Administrator 14 * 15 */ 16 public class Test001 { 17 public static void main(String[] args) throws IOException { 18 System.out.println("序列化之前"); 19 Address address = new Address("中国", "台湾", "台北"); 20 Employee employee = new Employee("夜雨梧桐", 25, address); 21 System.out.println("员工1的信息"); 22 System.out.println(employee); 23 System.out.println("序列化之后"); 24 ObjectOutputStream outputStream = null; 25 ObjectInputStream inputStream = null; 26 Employee employee2 = null; 27 try { 28 outputStream = new ObjectOutputStream(new FileOutputStream("employee.dat")); 29 outputStream.writeObject(employee); 30 inputStream = new ObjectInputStream(new FileInputStream("employee.dat")); 31 employee2 = (Employee) inputStream.readObject(); 32 } catch (FileNotFoundException e) { 33 e.printStackTrace(); 34 } catch (IOException exception) { 35 exception.printStackTrace(); 36 } catch (ClassNotFoundException e) { 37 e.printStackTrace(); 38 } finally { 39 outputStream.close(); 40 inputStream.close(); 41 } 42 if (employee2 != null) { 43 employee2.getAddress().setState("中国"); 44 employee2.getAddress().setProvince("四川"); 45 employee2.getAddress().setCity("成都"); 46 employee2.setName("刘伟"); 47 employee2.setAge(19); 48 System.out.println("员工1的信息"); 49 System.out.println(employee); 50 System.out.println("员工2的信息"); 51 System.out.println(employee2); 52 } 53 } 54 }
显示结果:
1 序列化之前 2 员工1的信息 3 Employee [name=夜雨梧桐, age=25, address=Address [State=中国, Province=台湾, City=台北]] 4 序列化之后 5 员工1的信息 6 Employee [name=夜雨梧桐, age=25, address=Address [State=中国, Province=台湾, City=台北]] 7 员工2的信息 8 Employee [name=刘伟, age=19, address=Address [State=中国, Province=四川, City=成都]]
当然,也可以使用ByteArrayOutputStream和ByteInputStream将对象保存在内存中,就不必产生一个本地文件保存对象;