java中有关深拷贝和浅拷贝的解析

 熟悉C++的朋友对这个话题应该很熟悉,浅拷贝就是指两个对象共同拥有同一个值,一个对象改变了该值,也会影响到另一个对象。深拷贝就是两个对象的值相等,但是互相独立。本来想把以前写的一篇文章扩充一下,没想到居然牵扯出很多复杂的问题。本文测试环境是windows xp sp3中文版、NetBeans6.7.1,JDK1.6-update16。这里抛砖引玉,希望大家能提宝贵意见。

    首先,Java中常用的拷贝操作有三个,operator = 、拷贝构造函数 和 clone()方法。由于Java不支持运算符重载,我们无法在自己的自定义类型中定义operator=。拷贝构造函数大家应该很熟悉,现在看一下如何支持clone方法:

  1. 实现 Cloneable接口,因为 Object的 clone方法将检查类是否实现了 Cloneable接口,如果没有将抛出异常 CloneNotSupportedException对象。 Cloneable接口没有任何方法,只是个标志,所以只需要简单的写上 implements Cloneable即可。

  2. 改写从 Object继承而来的 clone方法,使它的访问权限为 public,因为为了防止意外的支持 clone操作, Object的 clone方法是 protected权限。


    通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现Cloneable接口。
    现在,来看一下不同的类型在拷贝过程中的表现:


Operator =
拷贝构造函数
clone方法
预定义非集合类型
深拷贝
如果支持拷贝构造函数的类型,则是深拷贝
不支持
自定义类型
浅拷贝
取决于实现
取决于实现
预定义集合类型
浅拷贝
会逐个调用每个元素的operator=方法
会逐个调用每个元素的operator=方法


    下面是测试代码,首先测试的是预定义非集合类型的operator =操作:

[java]  view plain copy print ?
  1. int x=1;  
  2.        int y=x;  
  3.        y=2;  
  4.        if(x!=y){  
  5.            System.out.println("deep copy");  
  6.        }  
  7.   
  8.   
  9.        Integer a=1;  
  10.        Integer b=a;  
  11.        b=2;  
  12.        if(!a.equals(b)){  
  13.            System.out.println("deep copy");  
  14.        }  
  15.   
  16.   
  17.        String m="ok";  
  18.        String n=m;  
  19.        n="no";  
  20.        if(!m.equals(n)){  
  21.            System.out.println("deep copy");  
  22.        }  

 程序运行后,输出三行deep copy,测试结果表明,这三种类型的operator =操作都是深拷贝。由于我没有测试完所有的预定义非集合类型,我这里推测它们的operator =都是深拷贝。

    下面测试预定义非集合类型的拷贝构造函数:

[java]  view plain copy print ?
  1. Integer a=1;  
  2.        Integer b=new Integer(a);  
  3.        b=2;  
  4.        if(!a.equals(b)){  
  5.            System.out.println("deep copy");  
  6.        }  
  7.   
  8.   
  9.        String m="ok";  
  10.        String n=new String(m);  
  11.        n="no";  
  12.        if(!m.equals(n)){  
  13.            System.out.println("deep copy");  
  14.        }  

程序运行后,输出两行deep copy,测试结果表明,这两种类型的拷贝构造函数都是深拷贝。int没有拷贝构造函数。
    
        现在我们来测试自定义类型的operator=操作,假设我有一个类Person,代码如下:

[java]  view plain copy print ?
  1. public class Person implements Cloneable{  
  2.     private int age;  
  3.     private String name;  
  4.     public int getAge() {  
  5.         return age;  
  6.     }  
  7.     public void setAge(int age) {  
  8.         this.age = age;  
  9.     }  
  10.     public String getName() {  
  11.         return name;  
  12.     }  
  13.     void setName(String name) {  
  14.         this.name = name;  
  15.     }  
  16. }  

 测试代码:

[java]  view plain copy print ?
  1. Person p=new Person();  
  2.        p.setAge(32);  
  3.        p.setName("陈抒");  
  4.   
  5.        Person p2=p;  
  6.        p.setAge(33);  
  7.        p.setName("老陈");  
  8.   
  9.        if( (p.getAge()!=p2.getAge())&&(!p.getName().equals(p2.getName())) ){  
  10.            System.out.println("deep copy");  
  11.        }  

 运行后,没有输出deep copy,说明这是浅拷贝。这里就是我们常说的两个引用之间的赋值,仅仅是让两个引用指向同一个对象。

    现在,我们来测试预定义集合类型的operator=操作:

[java]  view plain copy print ?
  1. ArrayList list1=new ArrayList();  
  2.         list1.add("yangzhou");  
  3.         ArrayList list2=list1;  
  4.         list1.clear();  
  5.   
  6.         if(list2.isEmpty()){  
  7.             System.out.println("shallow copy");  
  8.         }  

  结果输出为shallow copy。

    现在我来测试拷贝构造函数:

[java]  view plain copy print ?
  1. ArrayList list1=new ArrayList();  
  2.         list1.add("yangzhou");  
  3.         ArrayList list2=new ArrayList(list1);  
  4.         list1.clear();  
  5.   
  6.         if(list2.isEmpty()){  
  7.             System.out.println("shallow copy");  
  8.         }else{  
  9.             System.out.println("deep copy");  
  10.         }  

输出结果是deep copy;
    clone方法的测试代码只是将第三行换成list1.clone(),加上类型转换,这里不再贴代码了。结果也证明是深拷贝 。
    预定义集合类的深拷贝 实际上就是调用每个元素的operator =。如果元素都是自定义类型的化,实际上还是浅拷贝。现在来看测试代码:

[java]  view plain copy print ?
  1. ArrayList list1=new ArrayList();  
  2.        Person p1=new Person();  
  3.        p1.setAge(32);  
  4.        p1.setName("陈抒");  
  5.        list1.add(p1);  
  6.   
  7.        ArrayList list2=(ArrayList) list1.clone();  
  8.        list2.get(0).setName("chenshu");  
  9.   
  10.        if(list2.get(0).getName().equals(list1.get(0).getName())){  
  11.            System.out.println("shallow copy");  
  12.        }else{  
  13.            System.out.println("deep copy");  
  14.   
  15.        }  

 输出为shallow copy,Person是自定义类型,它的operator =运算符只是引用之间赋值,是浅拷贝。因此当修改了list2的第一个元素指向的Person对象的name属性,也就是修改了list1第一个元素所指向的Person对象的name属性。对于这种拷贝,我自己起了一个名字,叫做第一层深拷贝。
    
    现在我们有了表格中的结论,自己实现拷贝构造函数或者clone方法的时候就心里有数多了。
    假如我的自定义类型内部成员变量都是预定义非集合类型,那么在clone方法中只需要调用Object.clone即可完成深拷贝操作。在拷贝构造函数中需要使用operator=来一个个的深拷贝;
    假如我们的自定义类型内部成员变量有一些预定义类型,另一些是自定义类型,如果要深拷贝的话,最好调用自定义类型成员变量的拷贝构造函数或者clone方法。下面是例子代码:

[java]  view plain copy print ?
  1. public class Company {  
  2.     public Company(){  
  3.   
  4.     }  
  5.   
  6.     public Company(Company c){  
  7.         name=c.name;  
  8.         person=new Person(c.person);  
  9.     }  
  10.     private String name;  
  11.     private Person person;  
  12.   
  13.     public Person getPerson() {  
  14.         return person;  
  15.     }  
  16.   
  17.     public void setPerson(Person person) {  
  18.         this.person = person;  
  19.     }  
  20.   
  21.     public String getName() {  
  22.         return name;  
  23.     }  
  24.   
  25.     public void setName(String name) {  
  26.         this.name = name;  
  27.     }  
  28.   
  29.     @Override  
  30.     public Object clone() throws CloneNotSupportedException{  
  31.         Company c=new Company();  
  32.         c.setName(name);  
  33.         c.setPerson((Person) person.clone());  
  34.         return c;  
  35.     }  
  36. }  
  37.   
  38. public class Person implements Cloneable{  
  39.     public Person(){  
  40.   
  41.     }  
  42.   
  43.     public Person(Person p){  
  44.         age=p.age;  
  45.         name=p.name;  
  46.     }  
  47.     private int age;  
  48.     private String name;  
  49.     public int getAge() {  
  50.         return age;  
  51.     }  
  52.     public void setAge(int age) {  
  53.         this.age = age;  
  54.     }  
  55.   
  56.     public String getName() {  
  57.         return name;  
  58.     }  
  59.   
  60.     public void setName(String name) {  
  61.         this.name = name;  
  62.     }  
  63.   
  64.       
  65.     @Override  
  66.     public Object clone() throws CloneNotSupportedException{  
  67.         return super.clone();  
  68.     }  
  69. }  

Person类的两个成员变量都是预定义非集合类型,所以只需要在clone方法中简单的调用super.clone()即可实现深拷贝。Company类有一个Person成员变量,因此要调用Person的clone方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值