java的clone()方法
什么是"clone"?
在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在 Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。
Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。
如何使用clone方法
首先需要实现CloseAble类
1 @Override 2 public Object clone() throws CloneNotSupportedException { 3 Student s = null; 4 try { 5 s = (Student) super.clone(); 6 } catch (CloneNotSupportedException e) { 7 e.printStackTrace(); 8 } 9 return s; 10 }
浅复制:
1、创建类Student
1 public class Student implements Cloneable { 2 3 private String name; 4 private int age; 5 private Professor professor; 6 7 public Student(String name, int age, Professor professor) { 8 this.name = name; 9 this.age = age; 10 this.professor = professor; 11 } 12 13 public String getName() { 14 return name; 15 } 16 17 public void setName(String name) { 18 this.name = name; 19 } 20 21 public int getAge() { 22 return age; 23 } 24 25 public void setAge(int age) { 26 this.age = age; 27 } 28 29 public Professor getProfessor() { 30 return professor; 31 } 32 33 public void setProfessor(Professor professor) { 34 this.professor = professor; 35 } 36 37 @Override 38 public String toString() { 39 return "Student{" + 40 "name='" + name + '\'' + 41 ", age=" + age + 42 ", professor=" + professor + 43 '}'; 44 } 45 46 @Override 47 public Object clone() throws CloneNotSupportedException { 48 Student s = null; 49 try { 50 s = (Student) super.clone(); 51 } catch (CloneNotSupportedException e) { 52 e.printStackTrace(); 53 } 54 return s; 55 } 56 }
2、创建类Professor
1 public class Professor{ 2 private String name; 3 private int age; 4 5 public Professor(String name, int age) { 6 this.name = name; 7 this.age = age; 8 } 9 10 public String getName() { 11 return name; 12 } 13 14 public void setName(String name) { 15 this.name = name; 16 } 17 18 public int getAge() { 19 return age; 20 } 21 22 public void setAge(int age) { 23 this.age = age; 24 } 25 26 @Override 27 public String toString() { 28 return "Professor{" + 29 "name='" + name + '\'' + 30 ", age=" + age + 31 '}'; 32 } 33 }
3、执行
1 @Test 2 public void cloneT(){ 3 Professor p1 = new Professor("Professor Zhang",30); 4 5 Student s1 = new Student("xiao ming",18,p1); 6 7 System.out.println(s1); 8 9 try { 10 Student s2 = (Student) s1.clone(); 11 s2.setName("xiao hong"); 12 s2.setAge(17); 13 Professor p2 = s2.getProfessor(); 14 p2.setName("Professor Li"); 15 p2.setAge(45); 16 s2.setProfessor(p2); 17 System.out.println("复制后的:s1 = " + s1); 18 System.out.println("复制后的:s2 = " + s2); 19 } catch (CloneNotSupportedException e) { 20 e.printStackTrace(); 21 } 22 23 }
4、运行结果:
1 Student [name=xiao ming, age=18, professor=Professor [name=Professor Zhang, age=30]] 2 复制后的:s1 = Student [name=xiao ming, age=18, professor=Professor [name=Professor Li, age=45]] 3 复制后的:s2 = Student [name=xiao hong, age=17, professor=Professor [name=Professor Li, age=45]]
问题:发现修改s2的Professor,对应的s1也变了,但是基本类型 name和age没有跟着改变。这主要是浅复制带来的问题,若要解决这个问题,需要使用深复制。
深复制:
1、为Professor 类添加clone方法
1 @Override 2 public Object clone() throws CloneNotSupportedException { 3 Professor p = null; 4 try { 5 p = (Professor) super.clone(); 6 } catch (CloneNotSupportedException e) { 7 e.printStackTrace(); 8 } 9 return p; 10 }
2、修改Student的clone方法
1 @Override 2 public Object clone() throws CloneNotSupportedException { 3 Student s = null; 4 try { 5 s = (Student) super.clone(); 6 s.professor = (Professor) this.professor.clone(); 7 } catch (CloneNotSupportedException e) { 8 e.printStackTrace(); 9 } 10 return s; 11 }
提示: 对于上面的解决方案还是存在一个问题,若我们系统中存在大量的对象是通过拷贝生成的,如果我们每一个类都写一个clone()方法,并将还需要进行深拷贝,新建大量的对象,这个工程是非常大的,这里我们可以利用序列化来实现对象的拷贝。
1 public class CloneUtils { 2 @SuppressWarnings("unchecked") 3 public static <T extends Serializable> T clone(T obj){ 4 T cloneObj = null; 5 try { 6 //写入字节流 7 ByteArrayOutputStream out = new ByteArrayOutputStream(); 8 ObjectOutputStream obs = new ObjectOutputStream(out); 9 obs.writeObject(obj); 10 obs.close(); 11 12 //分配内存,写入原始对象,生成新对象 13 ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); 14 ObjectInputStream ois = new ObjectInputStream(ios); 15 //返回生成的新对象 16 cloneObj = (T) ois.readObject(); 17 ois.close(); 18 } catch (Exception e) { 19 e.printStackTrace(); 20 } 21 return cloneObj; 22 } 23 }
使用该工具类的对象必须要实现Serializable接口,否则是没有办法实现克隆的。
1 public class Person implements Serializable{ 2 private static final long serialVersionUID = 2631590509760908280L; 3 4 .................. 5 //去除clone()方法 6 7 } 8 9 public class Email implements Serializable{ 10 private static final long serialVersionUID = 1267293988171991494L; 11 12 .................... 13 }
所以使用该工具类的对象只要实现Serializable接口就可实现对象的克隆,无须继承Cloneable接口实现clone()方法。
3、输出结果
1 Student [name=xiao ming, age=18, professor=Professor [name=Professor Zhang, age=30]] 2 复制后的:s1 = Student [name=xiao ming, age=18, professor=Professor [name=Professor Zhang, age=30]] 3 复制后的:s2 = Student [name=xiao hong, age=17, professor=Professor [name=Professor Li, age=45]]
注意:final 类型修饰的成员变量不能进行深度拷贝