浅克隆
如下图所示,浅克隆会复制基本类型的数据,原有对象和克隆对象会持有一个对引用类型的引用。
代码表现:
public class Email {
/** 邮件内容 */
private String content;
public Email(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public class User implements Cloneable {
private String name;
private Email email;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// set get construct方法省略
}
测试类
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
Email email = new Email("本周六加班: 上午10点到,下午6点下班");
User tom = new User("TOM", email);
User Lily = (User) tom.clone();
Lily.setName("Lily");
User Jack = (User) tom.clone();
Jack.setName("Jack");
System.out.println("###############################################");
System.out.println("## 邮件通知 ");
System.out.println("## ");
System.out.println("## " + tom.getName() + ": " + tom.getEmail().getContent());
System.out.println("## " + Lily.getName() + ": " + Lily.getEmail().getContent());
System.out.println("## " + Jack.getName() + ": " + Jack.getEmail().getContent());
System.out.println();
System.out.println("###############################################");
System.out.println("## 个人邮件通知 ");
System.out.println("## ");
Jack.getEmail().setContent("加个毛线班");
System.out.println("## " + tom.getName() + ": " + tom.getEmail().getContent());
System.out.println("## " + Lily.getName() + ": " + Lily.getEmail().getContent());
System.out.println("## " + Jack.getName() + ": " + Jack.getEmail().getContent());
}
}
结果:
可以看到,当我们修改其中某一个用户的引用类型Email 的时候,所有的人的Email 都变了。
深克隆
对Email 对象实现 Cloneable 方法并重写 clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
然后修改User中的clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
User user = (User) super.clone();
user.setEmail((Email) email.clone());
return user;
}
结果:
###############################################
## 邮件通知
##
## TOM: 本周六加班: 上午10点到,下午6点下班
## Lily: 本周六加班: 上午10点到,下午6点下班
## Jack: 本周六加班: 上午10点到,下午6点下班
###############################################
## 个人邮件通知
##
## TOM: 本周六加班: 上午10点到,下午6点下班
## Lily: 本周六加班: 上午10点到,下午6点下班
## Jack: 加个毛线班
总结:需要对深克隆对象中的引用类型实现Cloneable接口,并重写clone方法,并在被引用对象调用引用类型的clone方法,
User user = (User) super.clone();
user.setEmail((Email) email.clone());
方案二:引用类型实现序列化接口
implements Serializable
增加一个自定义deepclone0 方法
public User deepClone() throws IOException, ClassNotFoundException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream objOutStream = new ObjectOutputStream(baos); objOutStream.writeObject(this); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream objInputStream = new ObjectInputStream(bais); return (User) objInputStream.readObject(); }
原理:ObjectOutputStream 、ObjectInputStream 先将对象写入到磁盘,再通过流从磁盘中读到内存,这样每次读来的Object都是一个新的对象。