您的位置:首页 > 编程语言 > Java开发

Java的深拷贝和浅拷贝

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

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

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

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

通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现Cloneable接口。

现在,来看一下不同的类型在拷贝过程中的表现:

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

int x=1;

int y=x;

y=2;

if(x!=y){

System.out.println("deep copy");

}

Integer a=1;

Integer b=a;

b=2;

if(!a.equals(b)){

System.out.println("deep copy");

}

String m="ok";

String n=m;

n="no";

if(!m.equals(n)){

System.out.println("deep copy");

}

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

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

Integer a=1;

Integer b=new Integer(a);

b=2;

if(!a.equals(b)){

System.out.println("deep copy");

}

String m="ok";

String n=new String(m);

n="no";

if(!m.equals(n)){

System.out.println("deep copy");

}

程序运行后,输出两行deep copy,测试结果表明,这两种类型的拷贝构造函数都是深拷贝。int没有拷贝构造函数。

现在我们来测试自定义类型的operator=操作,假设我有一个类Person,代码如下:

public class Person implements Cloneable{

private int age;

private String name;

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String getName() {

return name;

}

void setName(String name) {

this.name = name;

}

}

测试代码:

Person p=new Person();

p.setAge(32);

p.setName("陈抒");

Person p2=p;

p.setAge(33);

p.setName("老陈");

if( (p.getAge()!=p2.getAge())&&(!p.getName().equals(p2.getName())) ){

System.out.println("deep copy");

}

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

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

ArrayList list1=new ArrayList();

list1.add("yangzhou");

ArrayList list2=list1;

list1.clear();

if(list2.isEmpty()){

System.out.println("shallow copy");

}

结果输出为shallow copy。

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

ArrayList list1=new ArrayList();

list1.add("yangzhou");

ArrayList list2=new ArrayList(list1);

list1.clear();

if(list2.isEmpty()){

System.out.println("shallow copy");

}else{

System.out.println("deep copy");

}

输出结果是deep copy;

clone方法的测试代码只是将第三行换成list1.clone(),加上类型转换,这里不再贴代码了。结果也证明是深拷贝 。

预定义集合类的深拷贝 实际上就是调用每个元素的operator =。如果元素都是自定义类型的化,实际上还是浅拷贝。现在来看测试代码:

ArrayList list1=new ArrayList();

Person p1=new Person();

p1.setAge(32);

p1.setName("陈抒");

list1.add(p1);

ArrayList list2=(ArrayList) list1.clone();

list2.get(0).setName("chenshu");

if(list2.get(0).getName().equals(list1.get(0).getName())){

System.out.println("shallow copy");

}else{

System.out.println("deep copy");

}

输出为shallow copy,Person是自定义类型,它的operator =运算符只是引用之间赋值,是浅拷贝。因此当修改了list2的第一个元素指向的Person对象的name属性,也就是修改了list1第一个元素所指向的Person对象的name属性。对于这种拷贝,我自己起了一个名字,叫做第一层深拷贝。

现在我们有了表格中的结论,自己实现拷贝构造函数或者clone方法的时候就心里有数多了。

假如我的自定义类型内部成员变量都是预定义非集合类型,那么在clone方法中只需要调用Object.clone即可完成深拷贝操作。在拷贝构造函数中需要使用operator=来一个个的深拷贝;

假如我们的自定义类型内部成员变量有一些预定义类型,另一些是自定义类型,如果要深拷贝的话,最好调用自定义类型成员变量的拷贝构造函数或者clone方法。下面是例子代码:

public class Company {

public Company(){

}

public Company(Company c){

name=c.name;

person=new Person(c.person);

}

private String name;

private Person person;

public Person getPerson() {

return person;

}

public void setPerson(Person person) {

this.person = person;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public Object clone() throws CloneNotSupportedException{

Company c=new Company();

c.setName(name);

c.setPerson((Person) person.clone());

return c;

}

}

public class Person implements Cloneable{

public Person(){

}

public Person(Person p){

age=p.age;

name=p.name;

}

private int age;

private String name;

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public Object clone() throws CloneNotSupportedException{

return super.clone();

}

}

Person类的两个成员变量都是预定义非集合类型,所以只需要在clone方法中简单的调用super.clone()即可实现深拷贝。Company类有一个Person成员变量,因此要调用Person的clone方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: