• Item 11 谨慎地覆盖Clone


        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 + "
    ");
        sb.append("Last Name: " + lastName + "
    ");
        sb.append("Street: " + address.street + "
    ");
        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

  • 相关阅读:
    Java中Collection和Collections的区别(转载)
    equals和==的区别
    【转载】String、StringBuffer与StringBuilder之间区别
    Mybatis实现
    springMVC---文件上传
    java---Map接口实现类
    java---迭代器(Iterator)
    java---泛型
    java---StringBuilder类的用法(转载)
    c#开源项目收集
  • 原文地址:https://www.cnblogs.com/ttylinux/p/4367239.html
Copyright © 2020-2023  润新知