将一个对象的引用复制给另一个对象,Java提供了三种复制方式,即:直接赋值、浅拷贝、深拷贝。
1、直接赋值
直接使用=赋值,即A a1 = a2,复制的是对象的引用,a1和a2指向的是同一个对象,当a1发生变化时,a2也会随之变化。
public class CopyTest {
public static void main(String[] args){
Person p1 = new Person();
Person p2 = p1;
System.out.println(p1);
System.out.println(p2);
}
}
输出结果为:com.cc.Person@1a93a7ca和com.cc.Person@1a93a7ca,可以看到是一样的,说明“=”赋值是引用复制,其实指向的是同一个对象。
2、浅拷贝和深拷贝
2.1 clone方法简介
对象调用clone方法时,会复制对象,JVM会分配一个和源对象一样大小的空间,然后在这个空间里创建一个对象。和new方法创建对象的方法,有所不同。
程序执行new方法时,会先看new操作符后面的类型,会根据具体类型,分配内存空间大小。分配完内存后,会调用构造函数,填充对象的各个域(初始化过程),构造方法返回后,对象new创建完毕,可以把对象的引用地址发布出去。
clone第一步也是分配内存,是根据源对象,分配一个一样的内存,然后再使用源对象各个域的值填充新对象的域,填充完成后,clone方法返回,新对象创建完成,可以将对象的引用地址发布到外部。
2.2 浅拷贝和深拷贝区别
首先我们可以先看下上面例子中的Person对象,有两个属性,age和name,如下代码:
public class Person implements Cloneable{
private String name;
private int age;
public Person(){}
public Person(int age,String name){
this.age = age;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return (Person)super.clone();
}
}
age是基本数据类型,拷贝没有什么过多的讨论,直接分配4字节的内存空间,值拷贝过来就可以了。name是个字符串类型,是个引用变量,存储了指向真正String对象的地址。
拷贝有两种方式:直接将源对象中的引用拷贝给新对象的name字段,即是浅拷贝;根据源对象的name对象,创建一个新的相同的字符串对象,将这个新的字符串对象的引用复制给新Person对象的name字段,即是深拷贝。
图如下:
代码验证如下:
public class CopyTest2 {
public static void main(String[] args) throws CloneNotSupportedException {
Person p = new Person(23,"chen");
Person p1 = (Person) p.clone();
System.out.println("p.getName().hashCode(): "+p.getName().hashCode());
System.out.println("p1.getName().hashCode(): "+p1.getName().hashCode());
System.out.println(p.getName().hashCode() == p1.getName().hashCode()?"clone是浅拷贝":"clone是深拷贝");
}
}
打印结果为:
p.getName().hashCode(): 3052494
p1.getName().hashCode(): 3052494
clone是浅拷贝
说明,clone是浅拷贝,String对象不可变性质,重新set时,就会实现String对象的重新创建。
2.3 深拷贝的实现
要实现对象的深拷贝,需要重写Cloneable接口的clone方法,不仅仅是要拷贝引用地址,还需要拷贝引用变量。
public class Address implements Cloneable{
private String city;
private String country;
public Address(String city,String country){
this.city = city;
this.country = country;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public Address clone() throws CloneNotSupportedException{
return (Address) super.clone();
}
}
public class User implements Cloneable{
private String name;
private Address address;
public User(String name,Address address){
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public User clone() throws CloneNotSupportedException{
User user = (User) super.clone();
user.setAddress(this.address.clone());
return user;
}
}
public class MyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("南京","中国");
User user = new User("cc",address);
User copyUser = user.clone();
copyUser.setName("tt");
copyUser.getAddress().setCity("扬州");
System.out.println("1"+user.getAddress().getCity());
System.out.println("2"+copyUser.getAddress().getCity());
System.out.println("3"+user.getName());
System.out.println("4"+copyUser.getName());
}
}