对象复制_js对象复制 - CSDN
精华内容
参与话题
  • 对象克隆复制)假如说你想复制一个简单变量。很简单:int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。但是如果你...

    对象克隆(复制)假如说你想复制一个简单变量。很简单:

    int apples = 5;  
    int pears = apples; 

    不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。

    但是如果你复制的是一个对象,情况就有些复杂了。

    假设说我是一个beginner,我会这样写:

    class Student {  
        private int number;  
    
        public int getNumber() {  
            return number;  
        }  
    
        public void setNumber(int number) {  
            this.number = number;  
        }  
    
    }  
    public class Test {  
    
        public static void main(String args[]) {  
            Student stu1 = new Student();  
            stu1.setNumber(12345);  
            Student stu2 = stu1;  
    
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
        }  
    }

    结果:

    学生1:12345

    学生2:12345

    这里我们自定义了一个学生类,该类只有一个number字段。

    我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)

    再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,

    难道真的是这样吗?

    我们试着改变stu2实例的number字段,再打印结果看看:

    stu2.setNumber(54321);  
    
    System.out.println("学生1:" + stu1.getNumber());  
    System.out.println("学生2:" + stu2.getNumber());  

    结果:

    学生1:54321

    学生2:54321

    这就怪了,为什么改变学生2的学号,学生1的学号也发生了变化呢?

    原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,

    这样,stu1和stu2指向内存堆中同一个对象。如图:
    这里写图片描述
    那么,怎样才能达到复制一个对象呢?

    是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。

    在Java中所有的类都是缺省的继承自Java语言包中的Object类的,查看它的源码,你可以把你的JDK目录下的src.zip复制到其他地方然后解压,里面就是所有的源码。发现里面有一个访问限定符为protected的方法clone():

    /*
    Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
    The general intent is that, for any object x, the expression:
    1) x.clone() != x will be true
    2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
    3) x.clone().equals(x) will be true, this is not an absolute requirement.
    */
    protected native Object clone() throws CloneNotSupportedException;

    仔细一看,它还是一个native方法,大家都知道native方法是非Java语言实现的代码,供Java程序调用的,因为Java程序是运行在JVM虚拟机上面的,要想访问到比较底层的与操作系统相关的就没办法了,只能由靠近操作系统的语言来实现。

    1)第一次声明保证克隆对象将有单独的内存地址分配。

    2)第二次声明表明,原始和克隆的对象应该具有相同的类类型,但它不是强制性的。

    3)第三声明表明,原始和克隆的对象应该是平等的equals()方法使用,但它不是强制性的。
    因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。

    要想对一个对象进行复制,就需要对clone方法覆盖。

    1、为什么要克隆

    大家先思考一个问题,为什么需要克隆对象?直接new一个对象不行吗?

    答案是:克隆的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠clone方法了。那么我把这个对象的临时属性一个一个的赋值给我新new的对象不也行嘛?可以是可以,但是一来麻烦不说,二来,大家通过上面的源码都发现了clone是一个native方法,就是快啊,在底层实现的。

    提个醒,我们常见的Object a=new Object();Object b;b=a;这种形式的代码复制的是引用,即对象在内存中的地址,a和b对象仍然指向了同一个对象。

    而通过clone方法赋值的对象跟原来的对象时同时独立存在的。

    2、如何实现克隆

    先介绍一下两种不同的克隆方法,浅克隆(ShallowClone)和深克隆(DeepClone)。

    在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制,下面将对两者进行详细介绍。

    一般步骤是(浅克隆):

    1) 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)

    2) 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)

    下面对上面那个方法进行改造:

    class Student implements Cloneable{  
        private int number;  
    
        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();  
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  
            return stu;  
        }  
    }  
    public class Test {  
        public static void main(String args[]) {  
            Student stu1 = new Student();  
            stu1.setNumber(12345);  
            Student stu2 = (Student)stu1.clone();  
    
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
    
            stu2.setNumber(54321);  
    
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
        }  
    }

    结果:

    学生1:12345

    学生2:12345

    学生1:12345

    学生2:54321

    如果你还不相信这两个对象不是同一个对象,那么你可以看看这一句:

    System.out.println(stu1 == stu2); // false  

    上面的复制被称为浅克隆

    还有一种稍微复杂的深度复制

    我们在学生类里再加一个Address类。

    class Address  {  
        private String add;  
    
        public String getAdd() {  
            return add;  
        }  
    
        public void setAdd(String add) {  
            this.add = add;  
        }  
    
    }  
    
    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();  
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  
            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());  
        }  
    }

    结果:

    学生1:123,地址:杭州市

    学生2:123,地址:杭州市

    乍一看没什么问题,真的是这样吗?

    我们在main方法中试着改变addr实例的地址。

    addr.setAdd("西湖区");  
    
    System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
    System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  

    结果:

    学生1:123,地址:杭州市
    学生2:123,地址:杭州市
    学生1:123,地址:西湖区
    学生2:123,地址:西湖区

    这就奇怪了,怎么两个学生的地址都改变了?

    原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象

    所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:

    
    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();   //浅复制  
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  
            stu.addr = (Address)addr.clone();   //深度复制  
            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());  
        }  
    }

    结果:

    学生1:123,地址:杭州市
    学生2:123,地址:杭州市
    学生1:123,地址:西湖区
    学生2:123,地址:杭州市

    这样结果就符合我们的想法了。

    最后我们可以看看API里其中一个实现了clone方法的类:

    java.util.Date:

    /** 
     * Return a copy of this object. 
     */  
    public Object clone() {  
        Date d = null;  
        try {  
            d = (Date)super.clone();  
            if (cdate != null) {  
                d.cdate = (BaseCalendar.Date) cdate.clone();  
            }  
        } catch (CloneNotSupportedException e) {} // Won't happen  
        return d;  
    }

    该类其实也属于深度复制

    3、深克隆和浅克隆

    1)浅克隆

    在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

    简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制
    这里写图片描述

    在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆

    2)深克隆

    在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象

    简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制

    这里写图片描述

    在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现

    如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。)

    序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

    扩展:

    Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。

    4、解决多层克隆问题

    如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。

    public class Outer implements Serializable{
      private static final long serialVersionUID = 369285298572941L;  //最好是显式声明ID
      public Inner inner;
     //Discription:[深度复制方法,需要对象及对象所有的对象属性都实现序列化] 
      public Outer myclone() {
          Outer outer = null;
          try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
              ObjectOutputStream oos = new ObjectOutputStream(baos);
              oos.writeObject(this);
          // 将流序列化成对象
              ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
              ObjectInputStream ois = new ObjectInputStream(bais);
              outer = (Outer) ois.readObject();
          } catch (IOException e) {
              e.printStackTrace();
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }
          return outer;
      }
    }

    Inner也必须实现Serializable,否则无法序列化:

    public class Inner implements Serializable{
      private static final long serialVersionUID = 872390113109L; //最好是显式声明ID
      public String name = "";
    
      public Inner(String name) {
          this.name = name;
      }
    
      @Override
      public String toString() {
          return "Inner的name值为:" + name;
      }
    }

    这样也能使两个对象在内存空间内完全独立存在,互不影响对方的值。

    实现对象克隆有两种方式:

    1). 实现Cloneable接口并重写Object类中的clone()方法;

    2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

    注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

    5、JAVA中JavaBean对象之间拷贝的方法

    JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyProperties方法进行。方法如下:

    User u = new User();//source,有三个属性uid,name,age
    u.setUid("1");
    u.setName("tom");
    u.setAge("18");
    
    Student s = new Student();//target,有三个属性sid,name,sex
    
    BeanUtils.copyProperties(u, s);
    System.out.println("[sid]"+s.getSid()+"#[name]"+s.getName()+"#[sex]"+s.getSex());

    输出如下:
    [sid]null#[name]tom#[sex]null

    总结:copyProperties方法的作用是将两个对象之间相同的属性进行复制。比如上面两个JavaBean对象只有name属性相同,因此只成功复制了name属性的值

    展开全文
  • js拷贝对象之——深拷贝、浅拷贝

    千次阅读 2018-09-07 11:05:41
    在js中,当我们对一个对象进行复制后会发现,改变复制对象内容时,被复制对象也进行了相同的改变。 这里就涉及到了一个深拷贝与浅拷贝的问题。深拷贝和浅拷贝是只针对像object、array这样复杂的对象。 js中的...

    在js中,当我们对一个对象进行复制后会发现,改变复制的对象内容时,被复制的对象也进行了相同的改变。
    这里就涉及到了一个深拷贝与浅拷贝的问题。深拷贝和浅拷贝是只针对像object、array这样复杂的对象。

    js中的对象分为基本类型和复合(引用)类型,前者存放在栈内存,后者存放在堆内存。
    堆内存用于存放由new创建的对象,栈内存存放一些基本类型的变量和对象的引用变量

    浅拷贝

    浅拷贝指的是:对对象地址的复制,不会进行递归复制,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址。
    eg:

    var arr1 = [1,2,3,4,5];
    var arr2 = arr1;
    arr1.push(6);
    console.log(arr1);  //123456
    console.log(arr2);  //123456

    在例子中可以发现,当我们将arr1复制给arr2后,改变arr2值后,arr1也进行了相应的改变。
    浅拷贝的对象只是拷贝了基本类型的数据,而引用类型数据,复制后也是发生引用,指向的地址还是原对象的地址。

    深拷贝

    深拷贝指的是:将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。并开辟了一块新的内存地址来存放复制的对象。

    当对对象a进行深拷贝给对象b后,之后无论是对a操作还是对b操作,都是指改变自己的内容。实现深拷贝的方法有很多种,如下:

    1、JSON.stringify/parse的方法

    let arr1 = [1,2,3,4];
    let arr2 = JSON.parse(JSON.stringify(arr1));
    arr2.push(5);
    console.log(arr1); //[1, 2, 3, 4]
    console.log(arr2); //[1, 2, 3, 4, 5]
    
    const obj1 = {a:'a',b:'b'};
    const obj2 = JSON.parse(JSON.stringify(obj1));
    
    0bj2.a = 'aa';
    
    console.log(0bj1); // {a:'a',b:'b'};
    console.log(0bj2); // {a:'aa',b:'b'};

    对于简单的对象使用该方法是可以正常进行深拷贝的,但是如果对象中含有function则会出现问题

    const obj = {
      name:'zhangsan',
      fn:function(){
        console.log('Hello World');
      }
    }
    console.log(obj); // {name: "zhangsan", fn: ƒ}
    const obj2 = JSON.parse(JSON.stringify(obj));
    console.log(obj2); // {name: "zhangsan"}

    JSON.stringify/parse实现深拷贝的时候,需要求目标对象(非 undefined,function)

    2、使用递归方法
    使用递归方法,就是对每一层的数据都实现一次 创建对象->对象赋值的操作

    function deepClone(item){
      const target = item.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
      for(let keys in item){ // 遍历目标
        if(item.hasOwnProperty(keys)){
          if(item[keys] && typeof item[keys] === 'object'){ // 如果值是对象,就递归一下
            target[keys] = item[keys].constructor === Array ? [] : {};
            target[keys] = deepClone(item[keys]);
          }else{ // 如果不是,就直接赋值
            target[keys] = item[keys];
          }
        }
      }
      return target;
    }
    
    const obj1 = {a:'a',b:'b'};
    const obj2 = deepClone(obj1);
    
    obj2.a = 'aa';
    
    console.log(obj1); // {a:'a',b:'b'};
    console.log(obj2); // {a:'aa',b:'b'};
    
    const obj3 = {
      name:'zhangsan',
      fn:function(){
        console.log('Hello World');
      }
    }
    console.log(obj3); // {name: "zhangsan", fn: ƒ}
    const obj4 = deepClone(obj3);
    console.log(obj4); // {name: "zhangsan", fn: ƒ}

    改函数可以深拷贝数组、对象、以及带函数的对象。

    注:如果是数组还可以使用很多方法进行深拷贝:

    3、使用es6的Array.from(针对数组)

    var arr1=[1,2,3];
    var arr2=Array.from(arr1);
    arr1.push(4);
    console.log(arr1);  //[1,2,3,4]
    console.log(arr2);  //[1,2,3,]
    arr2.push(5);
    console.log(arr1);  //[1,2,3,4]
    console.log(arr2);  //[1,2,3,5]

    4、使用es6的…(只能进行第一层的深拷贝)

    var arr1=[1,2,3];
    var arr2=[...arr1];
    arr1.push(4);
    console.log(arr1);  //[1,2,3,4]
    console.log(arr2);  //[1,2,3,]
    arr2.push(5);
    console.log(arr1);  //[1,2,3,4]
    console.log(arr2);  //[1,2,3,5]

    还有concat、selice也可以对数组进行深拷贝(只能进行第一层的深拷贝)

    总结

    1. 在对象中赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值
    2. 递归是做复杂深拷贝比较合理的方法
    3. JSON深拷贝只能是对象中没有function时可以使用
    4. 数组的深拷贝方法较多,但是大多是只能进行第一层的深拷贝
    展开全文
  • Java对象复制三种方式

    万次阅读 多人点赞 2019-06-11 09:13:54
    在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象...

    我们都是爱情的草,在寒冷中死去,在温暖中重生,四季轮回,为了爱情不计仇恨。——《我有一杯酒,可以蔚风尘》

    1、概述

    在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。例如下面程序展示的情况:

    class Student {  
        private int number;  
      
        public int getNumber() {  
            return number;  
        }  
      
        public void setNumber(int number) {  
            this.number = number;  
        }  
          
    }  
    public class Test {   
        public static void main(String args[]) {  
            Student stu1 = new Student();  
            stu1.setNumber(12345);  
            Student stu2 = stu1;  
            stu1.setNumber(54321);  
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
        }  
    }
    

    结果:

    学生1:54321
    学生2:54321

    为什么改变学生2的学号,学生1的学号也发生了变化呢?
    原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,
    这样,stu1和stu2指向内存堆中同一个对象。如图:

    这里写代码片

    那么,怎么能干干净净清清楚楚地复制一个对象呢。在 Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求有很多途径,
    (1)将A对象的值分别通过set方法加入B对象中;
    (2)通过重写java.lang.Object类中的方法clone();
    (3)通过org.apache.commons中的工具类BeanUtils和PropertyUtils进行对象复制;
    (4)通过序列化实现对象的复制。

    #2、将A对象的值分别通过set方法加入B对象中
    对属性逐个赋值,本实例为了演示简单就设置了一个属性:

    Student stu1 = new Student();  
    stu1.setNumber(12345);  
    Student stu2 = new Student();  
    stu2.setNumber(stu1.getNumber());
    

    我们发现,属性少对属性逐个赋值还挺方便,但是属性多时,就需要一直get、set了,非常麻烦。

    #3、重写java.lang.Object类中的方法clone()
    先介绍一下两种不同的克隆方法,浅克隆(ShallowClone)和深克隆(DeepClone)。

    在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制,下面将对两者进行详细介绍。

    ##3.1 浅克隆
    一般步骤:

    1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)

    2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)

    class Student implements Cloneable{  
        private int number;  
      
        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();  
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  
            return stu;  
        }  
    }  
    public class Test {  
        public static void main(String args[]) {  
            Student stu1 = new Student();  
            stu1.setNumber(12345);  
            Student stu2 = (Student)stu1.clone();  
              
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
              
            stu2.setNumber(54321);  
          
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
        }  
    }  
    

    结果:
    学生1:12345
    学生2:12345
    学生1:12345
    学生2:54321

    在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

    简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

    这里写图片描述
    在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆。
    ##3.2 深克隆

    package abc;  
      
    class Address {  
        private String add;  
      
        public String getAdd() {  
            return add;  
        }  
      
        public void setAdd(String add) {  
            this.add = add;  
        }   
    }  
      
    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();   //浅复制  
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }   
            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());  
        }  
    }
    

    结果:
    学生1:123,地址:杭州市
    学生2:123,地址:杭州市
    学生1:123,地址:西湖区
    学生2:123,地址:西湖区

    怎么两个学生的地址都改变了?

    原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。

    为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:

    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();   //浅复制  
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  
            stu.addr = (Address)addr.clone();   //深度复制  
            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());  
        }  
    }
    

    结果:
    学生1:123,地址:杭州市
    学生2:123,地址:杭州市
    学生1:123,地址:西湖区
    学生2:123,地址:杭州市

    在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

    简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
    这里写图片描述
    在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。

    (如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。)

    #4、工具类BeanUtils和PropertyUtils进行对象复制

    Student stu1 = new Student();  
    stu1.setNumber(12345);  
    Student stu2 = new Student(); 
    BeanUtils.copyProperties(stu2,stu1);
    

    这种写法无论多少种属性都只需要一行代码搞定,很方便吧!除BeanUtils外还有一个名为PropertyUtils的工具类,它也提供copyProperties()方法,作用与BeanUtils的同名方法十分相似,主要的区别在于BeanUtils提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而PropertyUtils不支持这个功能,但是速度会更快一些。在实际开发中,BeanUtils使用更普遍一点,犯错的风险更低一点。

    #5、通过序列化实现对象的复制
    序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

    展开全文
  • 对象的赋值和复制

    千次阅读 2019-05-18 13:35:15
    对象的赋值 1.赋值的一般形式:对象名 2...1.对象复制的一般形式:类名 对象名2(对象名1。 2.也可以通过这样的形式进行复制:类名 对象名2=对象名1。 3对象的复制是通过复制构造函数进行的。 如: Box(Box &b) ...

    对象的赋值
    1.赋值的一般形式:对象名 2=对象名 1,是通过赋值号进行赋值的。
    2. 进行赋值的两个对象必须是两个同类对象。
    3.赋值时,其中一个对象应是确定的,即这个对象的数据成员是确定的。

    对象的复制
    1.对象复制的一般形式:类名 对象名2(对象名1。
    2.也可以通过这样的形式进行复制:类名 对象名2=对象名1。
    3对象的复制是通过复制构造函数进行的。
    如:

    Box(Box &b)
    {
    	width = b.width;
    	length = b.length;
    	height = b.height;
    }
    

    如下是隐含使用到复制构造函数的情况:

    #include<iostream>
    using namespace std;
    class Sum
    {
    public:
    	Sum(int = 6, int = 6);//声明指定默认参数值的构造函数
    	void get_sum()//声明一个输出x+y的值的函数
    	{
    		cout << "x+y=" << x + y<<endl;
    	}
    
    	int x;
    	int y;
    };
    Sum::Sum(int a, int b)//在类外定义一个普通构造函数
    {
    	x = a;
    	y = b;
    }
    void get_x(Sum s)//定义一个输出x和y的函数,同时也利用复制构造函数将s1的值赋值给s
    {
    	cout<<"x=" << s.x<<" "<<"y="<<s.y<<endl;
    }
    int main()
    {
    	Sum s1(2000,19);
    	get_x(s1);
    	s1.get_sum();
    }
    
    展开全文
  • 对象复制和赋值

    2018-12-03 19:40:34
    对象复制是在对象声明时进行的操作,而赋值是在声明之后的操作。一般而言,复制的语法为A a(b),A为类,b为已经定义的对象,a为复制b的对象;赋值的语法为A a;a = b;需要先声明对象a,之后再对其进行赋值。 有...
  • Java对象复制四种方式

    千次阅读 2019-03-16 09:13:18
    在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象...
  • Java对象的快速复制的几种方式

    万次阅读 2018-07-28 10:46:12
    浅拷贝、深度复制、BeanUtils.copyProperties() 对象克隆是指创建一个新的对象,且新的对象的状态与原始对象的状态相同。当对克隆的新对象进行修改时,不会影响原始对象的状态。 注释:clone()是object类的...
  • 一个对象的类的clone()是protected的 ,如何拷贝这个对象 ,类不可修改
  • 克隆游戏对象与创建预制
  • Unity克隆游戏对象

    千次阅读 2017-08-19 20:52:14
    克隆对象
  • JAVA复制对象给另一个对象

    万次阅读 2012-03-23 16:46:29
    今天算是遇到一个问题了。相当苦恼。 需求如下:有2个相同id的对象,但它们的值...找了很多地方,发现有2种方法可以达到复制对象的目的,只是最后这种方法还是没有解决我的问题。仅此记下来吧。 1,克隆clone java
  • js 对象克隆的三种方式

    万次阅读 2018-03-08 16:22:09
    js 对象克隆 把原来对象的属性遍历一遍,赋给一个新的对象 //深复制对象方法 var cloneObj = function (obj) { var newObj = {}; if (obj instanceof Array) { newObj = []; } for (var key in ...
  • C# 浅克隆与深克隆

    万次阅读 2016-09-23 17:53:42
    如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象复制时只复制它本身和其中包含的值类型的成员...
  • JAVA对象的深度克隆

    千次阅读 2012-03-23 13:52:42
    有时候,我们需要把对象A的所有值复制对象B(B = A),但是这样用等号给赋值你会发现,当B中的某个对象值改变时,同时也会修改到A中相应对象的值!  也许你会说,用clone()不就行了?!你的想法只对了一半,...
  • js对象不像变量那样,简单的赋值就能进行克隆的,对象进行赋值时,只是创建了一个新的指针指向相同的存储空间。就像这样var a={a:1} var b=a; b.a=2这时输出a你会发现,a的值同样发生了改变。这一点在你进行数据操作...
  • JS对象深度克隆实现

    万次阅读 多人点赞 2016-10-07 16:11:42
    本文介绍了JavaScript对象深度克隆的实现,分析了针对原始类型(数值、字符串、布尔值)和对象类型(对象,函数、数组)实现深度克隆的方法,最后给出了一个通用的JS对象深度克隆的方法。
  • 简要介绍:js中的对象的赋值,其实是实现了对象的引用,在赋值对象上修改属性会影响到原来的对象。–ES5中我们可以通过递归函数来实现深层次的克隆。 –ES6中我们可以通过Object.assign的方式来实现深度克隆。1....
  • java中String对象克隆

    千次阅读 2018-08-11 20:37:57
    实际上是改变了克隆出来对象String类型成员的指向,不会影响被克隆对象的。 解释:如果原来对象的一个string变量进行初始化的时候,指向的是一个字符串常量,该字符串常量会被放到常量池中, 该string类型的引用将...
  • javascript的一切实例都是对象,只是对象之间稍有不同,分为原始类型和合成...既然对象分为这两类,他们之间的最大差别是复制克隆的差别。普通对象存储的是对象的实际数据,而引用对象存储的是对象的引用地址,而把对象
  • C#克隆一个对象

    千次阅读 2018-10-24 14:19:11
    获取被克隆对象的类型,生成一个实例。再将被克隆对象的所有可写属性值赋予新对象实例。 代码: public class CloneObject { /// &lt;summary&gt; /// 克隆一个对象 /// &lt;/summary&gt; //...
1 2 3 4 5 ... 20
收藏数 941,313
精华内容 376,525
关键字:

对象复制