Item 11 谨慎地覆盖Clone
2015-03-25 22:30 ttylinux 阅读(219) 评论(0) 编辑 收藏 举报1.进行浅拷贝时,只是复制原始数据类型的值,则可以通过覆盖Clone方法来达到。另外,在进行浅拷贝的时候,还要注意,成员对象中不应该要有引用类型,如果有引用类型,那么,进行了浅拷贝之后,两个对象将会共享成员引用所指向的对象,这会出现问题。所以,在这种情况下,干脆直接使用深拷贝,避免问题出现。2.对于深拷贝,也就是完全将对象的内容复制一份,则使用序列化来实现,也是为了避免覆盖Clone。
浅拷贝的例子:
这个例子,只是复制成员的值,成员的类型都是原始数据类型,不包含引用类型:
public class CloneTest1 { public static void main(String[] args) throws CloneNotSupportedException { Student student1 = new Student(); student1.setName("ZhangSan"); student1.setAge(20); Student student2 = new Student(); student2 = (Student) student1.clone(); System.out.println("拷贝得到的信息"); System.out.println(student2.getName()); System.out.println(student2.getAge()); System.out.println("-------------"); // 修改第二个对象的信息 student2.setName("LiSi"); student2.setAge(25); System.out.println("修改第二个对象的属性为lisi,25后:"); System.out.println("第一个对象:"); System.out.println(student1.getName()); System.out.println(student1.getAge()); System.out.println("第二个对象:"); System.out.println(student2.getName()); System.out.println(student2.getAge()); System.out.println("-------------"); // 说明两个引用student1和student2指向的是不同的对象 } } class Student implements Cloneable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public Student clone() throws CloneNotSupportedException { // 注意此处要把protected改为public Student student = (Student)super.clone(); return student; } }
输出:
拷贝得到的信息
ZhangSan
20
-------------
修改第二个对象的属性为lisi,25后:
第一个对象:
ZhangSan
20
第二个对象:
LiSi
25
-------------
浅拷贝例子2:
这个例子中,成员的类型,有引用类型
public class CloneTest2 { public static void main(String[] args) throws CloneNotSupportedException { Teacher teacher = new Teacher(); teacher.setName("Teacher Zhang"); teacher.setAge(40); StudentNew student1 = new StudentNew(); student1.setName("ZhangSan"); student1.setAge(20); student1.setTeacher(teacher); StudentNew student2 = (StudentNew) student1.clone(); System.out.println("拷贝得到的信息"); System.out.println(student2.getName()); System.out.println(student2.getAge()); System.out.println(student2.getTeacher().getName()); System.out.println(student2.getTeacher().getAge()); System.out.println("-------------"); // 修改老师的信息 teacher.setName("Teacher Zhang has changed"); System.out.println(student1.getTeacher().getName()); System.out.println(student2.getTeacher().getName()); // 两个引用student1和student2指向不同的两个对象 // 但是两个引用student1和student2中的两个teacher引用指向的是同一个对象 // 所以说明是浅拷贝 } } class Teacher { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class StudentNew implements Cloneable { private String name; private int age; private Teacher teacher; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } @Override public StudentNew clone() throws CloneNotSupportedException { StudentNew object = (StudentNew) super.clone(); return object; } }
输出结果:
拷贝得到的信息
ZhangSan
20
Teacher Zhang
40
-------------
Teacher Zhang has changed
Teacher Zhang has changed
解析:
Student1和Student2,是两个对象,执行浅拷贝之后,两个的成员的值是相等的。如果里面包含了引用类型的成员,那么,它们是指向同一个对象的。比如,引用类型teacher成员。因为指向同一个对象,所以对teacher的修改,会同时影响到Student1,Student2.这会出现很多意料不到的问题。对于这种情况,我倾向于,使用序列化,做一个深拷贝。
深拷贝,使用序列化实现的深拷贝:
使用序列化实现深拷贝,优点是,不用覆盖Clone(覆盖Clone方法,把握不好,会出现各种问题);缺点是:类成员如果有类类型,则都要实现序列化接口,这样才可以进行序列化,比如下面的例子。
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class JavaDeepClone { public static void main(String[] args) { // (1) create a Person object named Al Address address = new Address("305 West Fern Avenue", "Palmer", "Alaska"); Person al = new Person("Al", "Alexander", address); // (2) make a deep clone of Al Person neighbor = (Person) deepClone(al); // (3) modify the neighbor's attributes neighbor.firstName = "Martha"; neighbor.lastName = "Stewart"; // (4) show that it all worked System.out.print(neighbor); System.out.println(al); } /** * This method makes a "deep clone" of any object it is given. */ public static Object deepClone(Object object) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(object); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } } } /** * These classes implement Serializable so we can write them out and read them back in as a stream * of bytes. */ class Person implements Serializable { /** * */ private static final long serialVersionUID = 4112183016776552816L; String firstName, lastName; Address address; public Person(String firstName, String lastName, Address address) { this.firstName = firstName; this.lastName = lastName; this.address = address; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("First Name: " + firstName + "\n"); sb.append("Last Name: " + lastName + "\n"); sb.append("Street: " + address.street + "\n"); return sb.toString(); } } class Address implements Serializable { /** * */ private static final long serialVersionUID = -3711035200324594412L; String street, city, state; public Address(String street, String city, String state) { this.street = street; this.city = city; this.state = state; } }
输出结果:
First Name: Martha
Last Name: Stewart
Street: 305 West Fern Avenue
First Name: Al
Last Name: Alexander
Street: 305 West Fern Avenue
引用:
http://www.cnblogs.com/mengdd/archive/2013/02/20/2917971.html
http://alvinalexander.com/java/java-deep-clone-example-source-code