在Java中如果想将一个基本类型变量的值赋给另一个变量,直接使用等号解可以了,原始的版本改变了,副本并不会发生变化,如下:
int a=10; int b=a; a=9; System.out.println(a); //9 System.out.println(b); //10
但是如果想要复制的变量不是基本类型,而是引用类型的话,就会与上面的效果不同:
package demos; /** * Created by hu on 2016/3/26. */ public class People { private int age; private String name; public People(int age,String name){ this.age=age; this.name=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; } public String toString(){ return this.name+" is "+this.age; } public static void main(String[] args){ People p1=new People(12,"tom"); People p2=p1; System.out.println(p1); System.out.println(p2); p1.setAge(25); p1.setName("jack"); System.out.println(p1); System.out.println(p2); } } /*output: * tom is 12 * tom is 12 * jack is 25 * jack is 25 * */
在上面的代码中,原始对象发生变化,副本对象也发生了变化,这是因为,执行p2=p1这段代码的时候,只是发生了引用变量的复制,而并没有发生对象的复制,两个引用变量指向的还是同一个对象。
那么,怎样才能达到复制一个对象呢?
是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。
该方法的签名是:
protected native Object clone() throws CloneNotSupportedException;
一般步骤是(浅复制):
1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)
对上面的代码进行改造,如下:
package demos; /** * Created by hu on 2016/3/26. */ public class People implements Cloneable{ private int age; private String name; public People(int age,String name){ this.age=age; this.name=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; } public String toString(){ return this.name+" is "+this.age; } @Override public Object clone(){ People people = null; try{ people = (People)super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return people; } public static void main(String[] args){ People p1=new People(12,"tom"); People p2= (People) p1.clone(); System.out.println(p1); System.out.println(p2); p1.setAge(25); p1.setName("jack"); System.out.println(p1); System.out.println(p2); } } /*output: * tom is 12 * tom is 12 * jack is 25 * tom is 12 * */
那么我们还可以在People中再添加一个Address类,如下:
package demos; /** * Created by hu on 2016/3/26. */ class Address{ private String address; public Address(String address){ this.address=address; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString(){ return address; } } public class People implements Cloneable{ private int age; private String name; private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public People(int age,String name){ this.age=age; this.name=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; } public String toString(){ return this.name+" is "+this.age+", live in "+address; } @Override public Object clone(){ People people = null; try{ people = (People)super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return people; } public static void main(String[] args){ Address address=new Address("LuoYang"); People p1=new People(12,"tom"); p1.setAddress(address); People p2= (People) p1.clone(); System.out.println(p1); System.out.println(p2); p1.setAge(25); p1.setName("jack"); address.setAddress("ZhengZhou"); System.out.println(p1); System.out.println(p2); } } /*output: * tom is 12, live in LuoYang * tom is 12, live in LuoYang * jack is 25, live in ZhengZhou * tom is 12, live in ZhengZhou * */
上面的代码,原始的版本对象地址发生了变化,但是副本却没有发生变化,那是因为在进行复制时,只复制了address这个变量的引用,并没有复制该变量指向的对象。此时就需要深度复制,深度复制很简单,只需要将Address也实现Cloneable接口即可,修改如下:
package demos; /** * Created by hu on 2016/3/26. */ class Address implements Cloneable{ private String address; public Address(String address){ this.address=address; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString(){ return address; } @Override public Object clone(){ Address address = null; try{ address = (Address)super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return address; } } public class People implements Cloneable{ private int age; private String name; private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public People(int age,String name){ this.age=age; this.name=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; } public String toString(){ return this.name+" is "+this.age+", live in "+address; } @Override public Object clone(){ People people = null; try{ people = (People)super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } people.setAddress((Address) people.getAddress().clone()); return people; } public static void main(String[] args){ Address address=new Address("LuoYang"); People p1=new People(12,"tom"); p1.setAddress(address); People p2= (People) p1.clone(); System.out.println(p1); System.out.println(p2); p1.setAge(25); p1.setName("jack"); address.setAddress("ZhengZhou"); System.out.println(p1); System.out.println(p2); } } /*output: * tom is 12, live in LuoYang * tom is 12, live in LuoYang * jack is 25, live in ZhengZhou * tom is 12, live in LuoYang * */
如此,就是深度复制了。
总结:浅拷贝是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝,没有对引用指向的对象进行拷贝。而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝。区别就在于是否对对象中的引用变量所指向的对象进行拷贝。