深拷贝、浅拷贝和clone、new
-
对于基础类型,会拷贝具体的内容。
-
对于引用类型,存储的这个值只是指向实际对象的地址,拷贝也只会拷贝引用地址。
- 浅拷贝
- 对基本数据类型进行值传递
- 对引用数据类型进行引用地址的拷贝
- 深拷贝
- 对基本数据类型进行值传递
- 对引用数据类型,创建一个新的对象,并复制其内容
拷贝相关API
Object#clone
Java 中所有的对象都是继承自 java.lang.Object。Object 对象中提供了一个 protected 类型的 clone 方法。
protected native Object clone()
throws CloneNotSupportedException;
Object#clone() 方法是 native 的,所以不需要我们来实现。需要注意的是,clone 方法是 protected 的,这意味着 clone 方法只能在 java.lang 包或者其子类可见。如果我们想要在一个程序中调用某个对象的 clone 方法则是不可以的。因为 clone 方法是定义在 Object 中的,该对象并没有对外可见的 clone 方法。
Cloneable接口
在上文中提到,Object#clone() 方法是 protected 的,我们不能直接在程序中对一个对象调用 clone 方法。JDK 推荐「实现 Cloneable 接口并重写 clone 方法(可使用 public 修饰符)来实现属性的拷贝」。
package java.lang;
/**
* 此处给出 Cloneable 的部分注释
* A class implements the Cloneable interface to
* indicate to the java.lang.Object#clone() method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
*
* Invoking Object's clone method on an instance that does not implement the
* Cloneable interface results in the exception
* CloneNotSupportedException being thrown.
*
* By convention, classes that implement this interface should override
* Object.clone (which is protected) with a public method.
*/
public interface Cloneable {
}
阅读 Cloneable 源码,有如下结论:
- 对于实现
Cloneable接口的对象,是可以调用Object#clone()方法来进行属性的拷贝。 - 若一个对象没有实现
Cloneable接口,直接调用Object#clone()方法,会抛出CloneNotSupportedException异常。 Cloneable是一个空接口,并不包含clone方法。但是按照惯例(by convention),实现Cloneable接口时,应该以public修饰符重写Object#clone()方法(该方法在Object中是被protected修饰的)。
浅拷贝
参考 Cloneable 接口的源码注释部分,如果一个类实现了 Cloneable 接口,那么 Object 的 clone 方法将返回该对象的逐个属性(field-by-field)拷贝
- 对基本数据类型进行值传递
- 对引用数据类型进行引用地址的拷贝
- 定义两个对象,
Address和CustomerUser
@Data
class Address implements Cloneable{
private String name;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
@Data
class CustomerUser implements Cloneable{
private String firstName;
private String lastName;
private Address address;
private String[] cars;
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
testShallowCopy();
}
public static void testShallowCopy() throws CloneNotSupportedException {
Address address= new Address();
address.setName("北京天安门");
CustomerUser customerUser = new CustomerUser();
customerUser.setAddress(address);
customerUser.setLastName("李");
customerUser.setFirstName("雷");
String[] cars = new String[]{"别克","路虎"};
customerUser.setCars(cars);
//浅拷贝
CustomerUser customerUserCopy =(CustomerUser) customerUser.clone();
customerUserCopy.setFirstName("梅梅");
customerUserCopy.setLastName("韩");
customerUserCopy.getAddress().setName("北京颐和园");
customerUserCopy.getCars()[0]="奥迪";
System.out.println("customerUser: " + JSONUtil.toJsonStr(customerUser));
System.out.println("customerUserCopy: " + JSONUtil.toJsonStr(customerUserCopy));
}
}
- 程序运行结果如下。
customerUser: {"lastName":"李","address":{"name":"北京颐和园"},"firstName":"雷","cars":["奥迪","路虎"]}
customerUserCopy: {"lastName":"韩","address":{"name":"北京颐和园"},"firstName":"梅梅","cars":["奥迪","路虎"]}
深拷贝
实现深拷贝有两种方式,「序列化对象方式」和「二次调用 clone 方式」
- 序列化(
serialization)方式 - 先对对象进行序列化,再进行反序列化,得到一个新的深拷贝的对象
- 二次调用
clone方式 - 先调用对象的
clone()方法 - 对对象的引用类型的属性值,继续调用
clone()方法进行拷贝
修改 CustomerUser 的 clone() 方法,对 CustomerUser 对象的引用类型的属性值,即 Address 属性值和数组(String[])属性值 cars,二次调用 clone 方法。
@Data
class CustomerUser implements Cloneable{
private String firstName;
private String lastName;
private Address address;
private String[] cars;
@Override
public Object clone() throws CloneNotSupportedException{
CustomerUser customerUserDeepCopy = (CustomerUser) super.clone();
//二次调用clone方法
customerUserDeepCopy.address = (Address) address.clone();
customerUserDeepCopy.cars = cars.clone();
return customerUserDeepCopy;
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
testShallowCopy();
}
public static void testShallowCopy() throws CloneNotSupportedException {
Address address= new Address();
address.setName("北京天安门");
CustomerUser customerUser = new CustomerUser();
customerUser.setAddress(address);
customerUser.setLastName("李");
customerUser.setFirstName("雷");
String[] cars = new String[]{"别克","路虎"};
customerUser.setCars(cars);
//浅拷贝
CustomerUser customerUserCopy =(CustomerUser) customerUser.clone();
customerUserCopy.setFirstName("梅梅");
customerUserCopy.setLastName("韩");
customerUserCopy.getAddress().setName("北京颐和园");
customerUserCopy.getCars()[0]="奥迪";
System.out.println("customerUser: " + JSONUtil.toJsonStr(customerUser));
System.out.println("customerUserCopy: " + JSONUtil.toJsonStr(customerUserCopy));
}
}
- 运行程序,输出结果如下。
customerUser: {"lastName":"李","address":{"name":"北京天安门"},"firstName":"雷","cars":["别克","路虎"]}
customerUserCopy: {"lastName":"韩","address":{"name":"北京颐和园"},"firstName":"梅梅","cars":["奥迪","路虎"]}

浙公网安备 33010602011771号