java中的深复制与浅复制

本文详细介绍了Java中对象拷贝的概念,包括浅拷贝和深拷贝的区别与实现方式,并通过示例代码展示了如何实现这两种拷贝。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部 数据。Java中有三种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)、延迟拷贝(Lazy Copy)。

一、引言
   对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部 数据。Java中有三种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)、延迟拷贝(Lazy Copy)。

二、浅拷贝

1、什么是浅拷贝
   浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。


  在图中,SourceObject有一个int类型的属性 "field1"和一个引用类型属性"refObj"(引用ContainedObject类型的对象)。当对SourceObject做浅拷贝时,创建了CopiedObject,它有一个包含"field1"拷贝值的属性"field2"以及仍指向refObj本身的引用。由于"field1"是基本类型,所以只是将它的值拷贝给"field2",但是由于"refObj"是一个引用类型, 所以CopiedObject指向"refObj"相同的地址。因此对SourceObject中的"refObj"所做的任何改变都会影响到CopiedObject。

运行下面的程序,看一看浅拷贝:

 

class Professor0 implements Cloneable {
     String name;
     int age;
 
     Professor0(String name, int age) {
         this .name = name;
         this .age = age;
     }
 
     public Object clone() throws CloneNotSupportedException {
         return super .clone();
     }
}
 
class Student0 implements Cloneable {
     String name; // 常量对象。
     int age;
     Professor0 p; // 学生1和学生2的引用值都是一样的。
 
     Student0(String name, int age, Professor0 p) {
         this .name = name;
         this .age = age;
         this .p = p;
     }
 
     public Object clone() {
         Student0 o = null ;
         try {
             o = (Student0)super.clone();
         } catch (CloneNotSupportedException e) {
             System.out.println(e.toString());
         }
 
         return o;
     }
}
 
public class ShallowCopy {
     public static void main(String[] args) {
         Professor0 p = new Professor0( "wangwu" , 50 );
         Student0 s1 = new Student0( "zhangsan" , 18 , p);
         Student0 s2 = (Student0) s1.clone();
         s2.p.name = "lisi" ;
         s2.p.age = 30 ;
         s2.name = "z" ;
         s2.age = 45 ;
         System.out.println( "学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name + "," + "\n学生s1教授的年纪" + s1.p.age); // 学生1的教授
     }
}

s2变了,但s1也变了,证明s1的p和s2的p指向的是同一个对象。这在我们有的实际需求中,却不是这样,因而我们需要深拷贝:


三、深拷贝
1、什么是深拷贝
   深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。



在上图中,SourceObject有一个int类型的属性 "field1"和一个引用类型属性"refObj1"(引用ContainedObject类型的对象)。当对SourceObject做深拷贝时,创建了CopiedObject,它有一个包含"field1"拷贝值的属性"field2"以及包含"refObj1"拷贝值的引用类型属性"refObj2" 。因此对SourceObject中的"refObj"所做的任何改变都不会影响到CopiedObject


 

class Professor implements Cloneable {
     String name;
     int age;
 
     Professor(String name, int age) {
         this .name = name;
         this .age = age;
     }
 
     public Object clone() {
         Object o = null ;
         try {
             o = super .clone();
         } catch (CloneNotSupportedException e) {
             System.out.println(e.toString());
         }
         return o;
     }
}
 
class Student implements Cloneable {
     String name;
     int age;
     Professor p;
 
     Student(String name, int age, Professor p) {
         this .name = name;
         this .age = age;
         this .p = p;
     }
 
     public Object clone() {
         Student o = null ;
         try {
             o = (Student)super.clone();
         } catch (CloneNotSupportedException e) {
             System.out.println(e.toString());
         }
         o.p = (Professor) p.clone();
         return o;
     }
}
 
public class DeepCopy {
     public static void main(String args[]) {
         long t1 = System.currentTimeMillis();
         Professor p = new Professor( "wangwu" , 50 );
         Student s1 = new Student( "zhangsan" , 18 , p);
         Student s2 = (Student) s1.clone();
         s2.p.name = "lisi" ;
         s2.p.age = 30 ;
         System.out.println( "name=" + s1.p.name + "," + "age=" + s1.p.age); // 学生1的教授不改变。
         long t2 = System.currentTimeMillis();
         System.out.println(t2-t1);
     }
}

当然我们还有一种深拷贝方法,就是将对象串行化:

 

import java.io.*;
//Serialization is time-consuming
class Professor2 implements Serializable {
     /**
      *
      */
     private static final long serialVersionUID = 1L;
     String name;
     int age;
 
     Professor2(String name, int age) {
         this .name = name;
         this .age = age;
     }
}
 
class Student2 implements Serializable {
     /**
      *
      */
     private static final long serialVersionUID = 1L;
     String name; // 常量对象。
     int age;
     Professor2 p; // 学生1和学生2的引用值都是一样的。
 
     Student2(String name, int age, Professor2 p) {
         this .name = name;
         this .age = age;
         this .p = p;
     }
 
     public Object deepClone() throws IOException, OptionalDataException,
             ClassNotFoundException {
         // 将对象写到流里
         ByteArrayOutputStream bo = new ByteArrayOutputStream();
         ObjectOutputStream oo = new ObjectOutputStream(bo);
         oo.writeObject( this );
         // 从流里读出来
         ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
         ObjectInputStream oi = new ObjectInputStream(bi);
         return (oi.readObject());
     }
 
}
 
public class DeepCopy2 {
 
     /**
      * @param args
      */
     public static void main(String[] args) throws OptionalDataException,
             IOException, ClassNotFoundException {
         long t1 = System.currentTimeMillis();
         Professor2 p = new Professor2( "wangwu" , 50 );
         Student2 s1 = new Student2( "zhangsan" , 18 , p);
         Student2 s2 = (Student2) s1.deepClone();
         s2.p.name = "lisi" ;
         s2.p.age = 30 ;
         System.out.println( "name=" + s1.p.name + "," + "age=" + s1.p.age); // 学生1的教授不改变。
         long t2 = System.currentTimeMillis();
         System.out.println(t2-t1);
     }
 
}

但是串行化却很耗时,在一些框架中,我们便可以感受到,它们往往将对象进行串行化后进行传递,耗时较多。

---
可以转载, 但必须以超链接形式标明文章原始出处和作者信息及 版权声明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值