我继续在看java的基础知识,刚看完java的深克隆及浅克隆,就写篇博客记录下。
应用场景:
1.有时候,你需要克隆一个对象,但是对新克隆的对象的操作不会影响原对象。
2. 了解下深克隆,浅克隆,对java加深了解。
//对象引用的复制
Person p = new Person(23, "zhang");
Person p1 = p;
System.out.println(p);
System.out.println(p1);
结果是:
test.deepClone.Person@2a84aee7
test.deepClone.Person@2a84aee7
可以看出来两个对象的内存地址是一样的。那肯定是一个对象。p,p1只是两个引用而已。都指向了一个对象,可以把这种现象叫做引用的复制。
//对象的拷贝
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
System.out.println(p);
System.out.println(p1);
结果是:
test.deepClone.Person@2a84aee7
test.deepClone.Person@a09ee92
打印出不同的内存地址。说明实现了对象的克隆。
//person 类
public class Person implements Cloneable{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age, String name){
this.age = age;
this.name = name;
}
protected Object clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
记得实现Cloneable接口
我们为什么要实现Cloneable接口呢?其实Cloneable接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。
下面我再举例子来说明
public class MyTestCloneAbnormal implements Cloneable {
private String name;
private Email email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MyTestCloneAbnormal(String name, Email email) {
this.name = name;
this.email = email;
}
public Object clone() throws CloneNotSupportedException {
//返回clone的对象
return (MyTestCloneAbnormal) super.clone();
}
static class Email implements Cloneable{
private String contet;
private String title;
public String getContet() {
return contet;
}
public void setContet(String contet) {
this.contet = contet;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Email(String title, String content) {
this.title = title;
this.contet = content;
}
}
public static void main(String[] args) throws CloneNotSupportedException {
MyTestCloneAbnormal p = new MyTestCloneAbnormal("yu", new Email("9.6中午会议", "今天中午十二点,大家在3楼会议室开会"));
MyTestCloneAbnormal p1 = (MyTestCloneAbnormal) p.clone();
p1.setName("张三");
MyTestCloneAbnormal p2 = (MyTestCloneAbnormal) p.clone();
p2.setName("李四");
System.out.println("给[ " + p.getName() + " ]的邮件: " + p.email.getTitle() + "正文是:[" + p.email.getContet() + "]");
System.out.println("给[ " + p1.getName() + " ]的邮件: " + p1.email.getTitle() + "正文是:[" + p1.email.getContet() + "]");
System.out.println("给[ " + p2.getName() + " ]的邮件: " + p2.email.getTitle() + "正文是:[" + p2.email.getContet() + "]");
}
}
我是看的一个教程写的。如果你要给三个员工发邮件。clone p对象。得到p1,p2,打印的结果是:
给[ yu ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ 张三 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ 李四 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
如果我想让 yu早来半个小时,这个时候main方法是
public static void main(String[] args) throws CloneNotSupportedException {
MyTestCloneAbnormal p = new MyTestCloneAbnormal("yu", new Email("9.6中午会议", "今天中午十二点,大家在3楼会议室开会"));
MyTestCloneAbnormal p1 = (MyTestCloneAbnormal) p.clone();
p1.setName("张三");
MyTestCloneAbnormal p2 = (MyTestCloneAbnormal) p.clone();
p2.setName("李四");
System.out.println("给[ " + p.getName() + " ]的邮件: " + p.email.getTitle() + "正文是:[" + p.email.getContet() + "]");
System.out.println("给[ " + p1.getName() + " ]的邮件: " + p1.email.getTitle() + "正文是:[" + p1.email.getContet() + "]");
System.out.println("给[ " + p2.getName() + " ]的邮件: " + p2.email.getTitle() + "正文是:[" + p2.email.getContet() + "]");
//给p对象设置email内容
p.email.setContet("yu,你需要早到半小时");
System.out.println("给[ " + p.getName() + " ]的邮件: " + p.email.getTitle() + "正文是:[" + p.email.getContet() + "]");
System.out.println("给[ " + p1.getName() + " ]的邮件: " + p1.email.getTitle() + "正文是:[" + p1.email.getContet() + "]");
System.out.println("给[ " + p2.getName() + " ]的邮件: " + p2.email.getTitle() + "正文是:[" + p2.email.getContet() + "]");
}
结果如下所示,
给[ yu ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ 张三 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ 李四 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ yu ]的邮件: 9.6中午会议正文是:[yu,你需要早到半小时]
给[ 张三 ]的邮件: 9.6中午会议正文是:[yu,你需要早到半小时]
给[ 李四 ]的邮件: 9.6中午会议正文是:[yu,你需要早到半小时]
张三,李四的邮件内容也变了。。这就说明,关于对象clone,clone的是对象的引用。而不是clone的对象,这三个对象的email对象指向同一个地址。也就是所谓的浅克隆。
如果要实现深克隆,需要在克隆MyTestCloneAbnormal 类的时候,在MyTestCloneAbnormal类的clone处,克隆Email类。在Email类中也实现clone方法。代码如下所示。
MyTestCloneAbnormal类的clone方法
public Object clone() throws CloneNotSupportedException {
//返回clone的对象
// return (MyTestCloneAbnormal) super.clone();
MyTestCloneAbnormal newp = (MyTestCloneAbnormal) super.clone();
newp.email = (Email)email.clone();
return newp;
}
Email的clone类
@Override
protected Object clone() throws CloneNotSupportedException {
return (Email) super.clone();
}
结果如下所示:
给[ yu ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ 张三 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ 李四 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ yu ]的邮件: 9.6中午会议正文是:[yu,你需要早到半小时]
给[ 张三 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
给[ 李四 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]
这就说明,我们实现了上面三个对象的Email对象深克隆,三个对象的Email对象都是指向了不同的内存地址,所以说,上面我们对p的改动才没有影响到其他两个p1,p2对象的Email属性。
其实出现问题的关键就在于clone()方法上,我们知道该clone()方法是使用Object类的clone()方法,但是该方法存在一个缺陷,它并不会将对象的所有属性全部拷贝过来,而是有选择性的拷贝,基本规则如下:
1、 基本类型
如果变量是基本很类型,则拷贝其值,比如int、float等。
2、 对象
如果变量是一个实例对象,则拷贝其地址引用,也就是说此时新对象与原来对象是公用该实例变量。
3、 String字符串
若变量为String字符串,则拷贝其地址引用。但是在修改时,它会从字符串池中重新生成一个新的字符串,原有紫都城对象保持不变。
还有一种通过字节流拷贝的方式,有兴趣可以自己去搜下。
参考的文章列表
http://blog.csdn.net/chenssy/article/details/12952063(他贴的代码有点问题,其他的还ok)
http://blog.csdn.net/zhangjg_blog/article/details/18369201/