克隆的意义
如果一个对象A是引用类型,那么A中保存的信息是这个对象的堆地址,在程序中,有可能会出现这样的情况:某时某刻,我需要两个状态相同的对象,但是在此后的执行过程中分别执行不同的程序逻辑,然后得到两个状态不相同的对象。如果这时使用 B = A来得到A,B两个对象,是不行的,因为这两个对象实际上在内存中是指向同一个地址,之后的逻辑也针对这一个地址中的对象进行操作。这时候就需要用到clone()方法。
简单实现克隆
clone()方法是Object类中的一个受保护的,native的方法,并且要抛出一个CloneNotSupportedException,受保护说明这个clone只能被同一个包或者Object类的子类对象调用,native就先不考虑方法的底层实现,抛出CloneNotSupportedException异常,一般是两种情况,第一是要实现克隆的类没有实现Cloneable接口(提一句,Cloneable是一个标记接口,里面并没有需要实现的方法,只是告诉虚拟机一个信息,要有这个概念),会抛出异常,第二种是实现克隆的类中有域无法克隆(这种情况我还没有实际实现过)
import entity.User;
public class ZhengZe {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User(1234,"zhangsan");
user.clone();
}
}
package entity;
public class User implements Cloneable{
private Integer id ;
private String name;
public User() {
}
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
上面代码是很简单实现克隆的方式,可以看到User实体类中重写了clone方法,并且实现了Cloneable接口,实现接口的作用是为了不报异常,重写是因为Object类中clone方法是protected修饰,main方法所在的类ZhengZe不是java.lang 包中的类,并且ZhengZe是和User类一样继承Object类,子类中是无法调用相同父类对应的其他子类的protected修饰的方法的,所以必须在User类中重写clone方法。(这个地方不好理解,建议复习下修饰符相关知识)
实现深度克隆
深度克隆是什么意思呢,我们上面实现的克隆有个问题,现在User类中有id有name字段,假如说还有个地址address字段,地址需要分为省和市两部分,那address应该是个对象类型,这时通过clone克隆出来的对象,address的值和原对象的值是同一个地址,如果修改了其中一个对象的address,那么两个对象的address字段都会变化,这显然不是我们想要的结果,所以可以通过深度克隆解决这个问题。
import entity.Address;
import entity.User;
public class ZhengZe {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User(1234,"zhangsan",new Address("河北省","石家庄市"));
User copy = (User) user.clone();
System.out.println(user);
System.out.println(user.getAddress());
System.out.println(copy);
System.out.println(copy.getAddress());
}
}
package entity;
public class User implements Cloneable{
private Integer id ;
private String name;
private Address address;
public User() {
}
public User(Integer id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
User user = (User)super.clone();
user.address = (Address) user.address.clone();
return user;
}
}
package entity;
public class Address implements Cloneable{
private String province;
private String city;
public Address() {
}
public Address(String province, String city) {
this.province = province;
this.city = city;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
上述代码中使用了深度克隆的方式,可以看到Address类中也重写了clone方法,然后User类中的clone方法进行更新,将克隆对象的Address也进行克隆,根据输出的地址可以判断,无论是User对象,还是Address对象,都是不相同的,也可以修改代码测试不克隆Address类对象的情况,这里不赘述了。
所以在实际的应用中,要注意的几点:
- 类是否支持克隆
- 实现Cloneable接口
- 重写clone方法
- 类的域中是否有可变对象,如果有影响,需要进行深度克隆