zoukankan      html  css  js  c++  java
  • Java中的克隆

    在Java中赋值是很常用的

    //原始类型
    int a = 1;
    int b = a;
    //引用类型
    String[] weekdays = new String[5];
    String[] gongzuori = weekdays; //仅拷贝引用
    /*
    *在上述代码中如果是原始数据类型,赋值传递的为真实的值;如果是引用类型,赋值传递的是对象的引用,而不是对象
    */

    Clone
        在Java中,clone是将已有的对象在内存中赋值到另一个与之相同的对象的过程。Java中的克隆为逐域复制。
        在Java中药支持clone方法,需要首先实现Cloneable接口,此接口不包含任何方法,仅仅是一个标记接口。
        需要注意的是:如果想要支持clone,就需要实现Cloneable接口,如果没有实现Cloneable接口调用clone()方法,会抛出CloneNotSupportedException异常。
        然后是重写clone方法,并修改成public访问级别:

    static class CloneableImp implements Cloneable {
      public int count;
      public Child child;
          
          
      @Override
      public Object clone() throws CloneNotSupportedException {
          return super.clone();
      }
    }
    //调用clone方法赋值对象
    CloneableImp imp1 = new CloneableImp();
    imp1.child = new Child("Andy");
    try {
      Object obj = imp1.clone();
      CloneableImp imp2 = (CloneableImp)obj;
      System.out.println("main imp2.child.name=" + imp2.child.name);
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }

    浅拷贝
        上面的代码实现的clone实际上是属于浅拷贝(Shallow Copy)。
        关于浅拷贝,你该了解的
            使用默认的clone方法
            对于原始数据域进行值拷贝
            对于引用类型仅拷贝引用
            执行快,效率高
            不能做到数据的100%分离。
            如果一个对象只包含原始数据域或者不可变对象域,推荐使用浅拷贝。
        关于无法做到数据分离,我们可以使用这段代码验证

    CloneableImp imp1 = new CloneableImp();
    imp1.child = new Child("Andy");
    try {
      Object obj = imp1.clone();
      CloneableImp imp2 = (CloneableImp)obj;
      imp2.child.name = "Bob";
              
      System.out.println("main imp1.child.name=" + imp1.child.name);
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }

    上述代码我们使用了imp1的clone方法克隆出imp2,然后修改 imp2.child.name 为 Bob,然后打印imp1.child.name 得到的结果是

    main imp1.child.name=Bob
    //原因是浅拷贝并没有做到数据的100%分离,imp1和imp2共享同一个Child对象,所以一个修改会影响到另一个。

    深拷贝
        深拷贝可以解决数据100%分离的问题。只需要对上面代码进行一些修改即可。
        1、Child实现Cloneable接口。

    public class Child implements  Cloneable{
    
      public String name;
    
      public Child(String name) {
          this.name = name;
      }
    
      @Override
      public String toString() {
          return "Child [name=" + name + "]";
      }
    
      @Override
      protected Object clone() throws CloneNotSupportedException {
          return super.clone();
      }
    }

    2.重写clone方法,调用数据域的clone方法。

    static class CloneableImp implements Cloneable {
      public int count;
      public Child child;
          
          
      @Override
      public Object clone() throws CloneNotSupportedException {
          CloneableImp obj = (CloneableImp)super.clone();
          obj.child = (Child) child.clone();
          return obj;
      }
    }

    当我们再次修改imp2.child.name就不会影响到imp1.child.name的值了,因为imp1和imp2各自拥有自己的child对象,因为做到了数据的100%隔离。
    关于深拷贝的一些特点
        需要重写clone方法,不仅仅只调用父类的方法,还需调用属性的clone方法
        做到了原对象与克隆对象之间100%数据分离
        如果是对象存在引用类型的属性,建议使用深拷贝
        深拷贝比浅拷贝要更加耗时,效率更低
        Copy constructors
    使用复制构造器也可以实现对象的拷贝。
        复制构造器也是构造器的一种
        只接受一个参数,参数类型为当前的类
        目的是生成一个与参数相同的新对象
    复制构造器相比clone方法的优势是简单,易于实现。
    一段使用了复制构造器的代码示例

    public class Car {
      Wheel wheel;
      String manufacturer;
      
      public Car(Wheel wheel, String manufacturer) {
          this.wheel = wheel;
          this.manufacturer = manufacturer;
      }
      
      //copy constructor
      public Car(Car car) {
          this(car.wheel, car.manufacturer);
      }
      
      public static class Wheel {
          String brand;
      }
    }
    //注意,上面的代码实现为浅拷贝,如果想要实现深拷贝,参考如下代码
    public Car(Car car) {
      Wheel wheel = new Wheel();
      wheel.brand = car.wheel.brand;
          
      this.wheel = wheel;
      this.manufacturer = car.manufacturer;
    }

    为了更加便捷,我们还可以为上述类增加一个静态的方法

    public static Car newInstance(Car car) {
      return new Car(car);
    }

    使用Serializable实现深拷贝
    其实,使用序列化也可以实现对象的深拷贝。简略代码如下

    public class DeepCopyExample implements Serializable{
      private static final long serialVersionUID = 6098694917984051357L;
      public Child child;
      
      public DeepCopyExample copy() {
          DeepCopyExample copy = null;
          try {
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
              ObjectOutputStream oos = new ObjectOutputStream(baos);
              oos.writeObject(this);
      
              ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
              ObjectInputStream ois = new ObjectInputStream(bais);
              copy = (DeepCopyExample) ois.readObject();
          } catch (IOException e) {
              e.printStackTrace();
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }
          return copy;
      }
    }

    其中,Child必须实现Serializable接口

    public class Child implements Serializable{
      private static final long serialVersionUID = 6832122780722711261L;
      public String name = "";
    
      public Child(String name) {
          this.name = name;
      }
    
      @Override
      public String toString() {
          return "Child [name=" + name + "]";
      }
    }
    package abc;  
      
    class Address implements Cloneable {  
        private String add;  
      
        public String getAdd() {  
            return add;  
        }  
      
        public void setAdd(String add) {  
            this.add = add;  
        }  
          
        @Override  
        public Object clone() {  
            Address addr = null;  
            try{  
                addr = (Address)super.clone();  
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  
            return addr;  
        }  
    }  
      
    class Student implements Cloneable{  
        private int number;  
      
        private Address addr;  
          
        public Address getAddr() {  
            return addr;  
        }  
      
        public void setAddr(Address addr) {  
            this.addr = addr;  
        }  
      
        public int getNumber() {  
            return number;  
        }  
      
        public void setNumber(int number) {  
            this.number = number;  
        }  
          
        @Override  
        public Object clone() {  
            Student stu = null;  
            try{  
                stu = (Student)super.clone();   //浅复制 调用父类clone()方法 复制学生类对象 
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  
            stu.addr = (Address)addr.clone();   //深度复制  复制地址值 Address类重写了clone()方法  所以当调用此处clone()方法时,会深度调用Address类对象
            return stu;  
        }  
    }  
    public class Test {  
          
        public static void main(String args[]) {  
              
            Address addr = new Address();  
            addr.setAdd("杭州市");  
            Student stu1 = new Student();  
            stu1.setNumber(123);  
            stu1.setAddr(addr);  
              
            Student stu2 = (Student)stu1.clone();  
              
            System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
            System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
              
            addr.setAdd("西湖区");  
              
            System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
            System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
        }  
    }
    class AddressTest implements Cloneable{
        String add;
        public AddressTest(String add){
            this.add = add;
        }
        //重写clone()方法
        public Object clone(){
            AddressTest obj = null;
            try {
                obj = (AddressTest)super.clone();
            } catch (CloneNotSupportedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            return obj;
        }
        @Override
        public String toString() {
            // TODO 自动生成的方法存根
            return add;
        }
    }
    class StudentTest implements Cloneable{
        String name;
        AddressTest add;
        public StudentTest(String name, AddressTest add) {
            this.name = name;
            this.add = add;
        }
        public Object clone(){
            StudentTest stu = null;
            try {
                stu = (StudentTest)super.clone();//浅拷贝
                stu.add = (AddressTest)add.clone();//深度拷贝,add所在类中的clone()方法是重写过的
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            
            return stu;
        }
        @Override
        public String toString() {
            return name + "------" + add;
        }
        
    }
    public class CloneDemo {
        public static void main(String[] args) {
            AddressTest a1 = new AddressTest("杭州");
            AddressTest a2 = new AddressTest("北京");
            StudentTest s1 = new StudentTest("zed1",a2);
            StudentTest s2 = (StudentTest)s1.clone();
            s1.add = a1;
            System.out.println("s1: " + s1.toString());
            System.out.println("s2: " + s2.toString());
        }
    }
    class AddressTest /*implements Cloneable*/{
        String add;
        public AddressTest(String add){
            this.add = add;
        }
        //重写clone()方法
    //    public Object clone(){
    //        AddressTest obj = null;
    //        try {
    //            obj = (AddressTest)super.clone();
    //        } catch (CloneNotSupportedException e) {
    //            // TODO 自动生成的 catch 块
    //            e.printStackTrace();
    //        }
    //        return obj;
    //    }
        @Override
        public String toString() {
            // TODO 自动生成的方法存根
            return add;
        }
    }
    class Name{
        int count;
        String name;
        public Name(String name){
            this.name = name;
        }
        @Override
        public String toString() {
            // TODO 自动生成的方法存根
            return name;
        }
    }
    class StudentTest implements Cloneable{
        Name name;
        AddressTest add;
        public StudentTest(Name name){
            this.name = name;
        }
        public StudentTest(Name name, AddressTest add) {
            this.name = name;
            this.add = add;
        }
    
        public Object clone(){
            StudentTest stu = null;
            try {
                stu = (StudentTest)super.clone();//浅拷贝
                //stu.add = (AddressTest)add.clone();//深度拷贝,add所在类中的clone()方法是重写过的
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            
            return stu;
        }
        @Override
        public String toString() {
            return name + "------" + add;
        }
        
    }
    public class CloneDemo {
        public static void main(String[] args) {
            AddressTest a1 = new AddressTest("杭州");
            AddressTest a2 = new AddressTest("北京");
            Name n1 = new Name("zed1");
    
            StudentTest s1 = new StudentTest(n1,a1);
            //s1.name = new Name("zed2");
            StudentTest s2 = s1;
            //都会输出a2的值,所以是浅拷贝,a2并没有深度拷贝
            s2.add = a2;
            System.out.println("s1: " + s1.toString());
            System.out.println("s2: " + s2.toString());
        }
    }

       深拷贝和浅拷贝都是对象拷贝,不同的是对象类中存在其他类对象,他们都是拷贝这个对象,不同的是深拷贝拷贝的是这个对象,浅拷贝拷贝的是这个对象的引用地址。也就是浅拷贝如果更改类中引用的对象,那个地址的对象就会改变(浅拷贝的两个对象中的对象引用指向同一地址,相当于共享对象),而深拷贝就不会。所以深拷贝会在拷贝的时候处理对象中的引用对象。

  • 相关阅读:
    力扣(LeetCode)验证回文串 个人题解(C++)
    力扣(LeetCode)平方数之和 个人题解
    Exclusive Access 2 UVA
    C语言中指针*p[N], (*P)[N]的区别
    2018年蓝桥杯国赛比赛心得
    2018acm/icpc西安邀请赛比赛心得
    [最小割]Cable TV Network UVA
    Tree Reconstruction UVA
    Twenty Questions UVA
    python中的enumerate 函数(编号的实现方式)
  • 原文地址:https://www.cnblogs.com/changzuidaerguai/p/7109533.html
Copyright © 2011-2022 走看看