关于深、浅clone的描述
浅克隆:对当前对象进行克隆,并克隆该对象所包括的8种基本数据类型
和String类型
属性(copy一份该对象并重新分配内存,产生新的对象),但如果被克隆的对象还包括除了8种基本类型和String类型之外的其他类型属性,则浅克隆不会克隆这些属性(不会为这些属性分配内存,而是引用原来的属性
)。
深克隆:在浅克隆的基础上,递归的克隆会为所有类型属性重新分配内存,而不是像浅clone一样:8种基本类型 + String类型
以外的属性引用原对象的属性。
实现clone的要求
要使对象实现克隆,需要让对象实体类实现Cloneable
接口,否则实现类中调用重写的clone()方法会报CloneNotSupportedException
异常。抛出该异常表示:调用了Object类的clone()方法克隆了一个对象,但是该对象的类没有实现Cloneable接口。(覆盖clone()方法的应用程序也可以抛出此异常,表示无法或不应clone对象)
Cloneable接口是一个标记接口
,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功。
代码案例
说明:对user对象设计深浅clone逻辑,并且运行测试用例验证clone原理。
User类的基本设计:
java
@Getter
@Setter
public class User implements Serializable {
private static final long serialVersionUID = -1241246044434342674L;
private String username;
private String gender;
private Date createTime;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
return Objects.equals(username, user.username) && Objects.equals(gender, user.gender) && Objects.equals(createTime, user.createTime);
}
@Override
public int hashCode() {
return Objects.hash(username, gender, createTime);
}
}
重写Object的clone()方法但不实现Cloneable接口
在User类中重写clone()方法:
java
@Override
public Object clone() throws CloneNotSupportedException {
// 此处直接继承的Object的clone()方法,为浅clone
return super.clone();
}
测试用例:
java
@Test
public void testShallowClone() {
try {
User user = new User();
user.setUsername("Black");
user.setGender("Male");
user.setCreateTime(new Date());
User cloneUser = (User) user.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
执行结果: (抛出CloneNotSupportedException异常)
浅clone(已实现Cloneable接口)
在User类中实现Cloneable接口:
java
@Getter
@Setter
public class User implements Cloneable, Serializable {
// User类的基本设计,省略
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试用例:
java
try {
Date currentTime = new Date();
User user = new User();
user.setUsername("Black");
user.setGender("Male");
user.setCreateTime(currentTime);
User cloneUser = (User) user.clone();
System.out.println("user: " + JSON.toJSONString(user) + ", type: " + user.getClass().getName());
System.out.println("cloneUser: " + JSON.toJSONString(cloneUser) + ", type: " + user.getClass().getName());
System.out.println(user.equals(cloneUser));
currentTime.setTime(currentTime.getTime() + 1000);
System.out.println("----------");
System.out.println("user: " + JSON.toJSONString(user) + ", type: " + user.getClass().getName());
System.out.println("cloneUser: " + JSON.toJSONString(cloneUser) + ", type: " + user.getClass().getName());
System.out.println(user.equals(cloneUser));
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
执行结果: (更改Date类型的createTime值之后,两个对象仍相同,说明引用的是原先的属性。)
深clone
重写User类中的clone()方法:
java
@Override
public Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
User user = (User) obj;
user.createTime = (Date) this.createTime.clone();
return obj;
}
测试用例:(同浅clone)
执行结果:
(更改Date类型的createTime值之后,两个对象不相同了,说明给clone对象重新分配了内存。)
小结
- 要实现clone,除了需要重写Object的clone()方法外,还需要实现Cloneable接口,以向 java.lang.Objectclone() 方法表示该方法创建该类实例的逐个字段副本是合法的;
- 在未实现 Cloneable 接口的实例上调用 Object 的 clone() 方法会导致抛出CloneNotSupportedException异常;
- 实现此接口的类应使用公共方法(修改访问权限为
public
)覆盖 Object.clone()(clone()方法的默认访问权限为protected
,即在同一包内的类及不同包中的子类
可见)。