克隆,也叫复制,包括:深克隆、浅克隆(深复制或浅复制)。java的clone()方法定义在Object类中,克隆方法将对象复制一份返回给调用者。
一般而言:
1、对任意的对X象,都有X.clone() != X;
克隆对象与原对象不是一个对象
2、对任何对象X,都有X.getClass == X.clone().getClass();
克隆对象与原对象的类型一样
3、如果X的equals()方法定义恰当,那么X.clone().equals(X)应该成立。
【浅复制】
被复制的所有变量都与原来对象中的变量有相同的值,而所有的对其他对象的引用仍指向原来的对象。如图:
【深复制】
被复制的所有变量都与原来对象中的变量有相同的值,而所有的对其他对象的引用指向原来的对象所复制的一份新对象。如图:
克隆的准备条件:
1、要被克隆的对象必须implements Cloneable;
2、复写Object类中的clone()方法,Object类中的clone()方法是protected类型的,在复写时要改成public类型的;
3、在复写的clone()方法中,第一行写super.clone();原因如下:
通过调用super.clone();在运行时,Object类的clone()方法会自动识别出要复制的是哪个对象,然后为此对象分配空间,将原始对象中的内容一一复制到新对象的存储空间中。
注意:继承Object类中的clone()方法是浅复制。
深克隆、浅克隆示例
【浅克隆】
package com.lpp.clone;
public class CloneDemo
{
public static void main(String[] args) throws Exception
{
Children c = new Children();
c.setAge(16);
c.setName("children1");
Person p = new Person();
p.setAge(28);
p.setName("lpp");
p.setChildren(c);
Person p2 = (Person)p.clone();
//打印新对象中的内容
System.out.println(p2.getName()+":"+p2.getAge());
System.out.println(p2.getChildren().getName()+":"+p2.getChildren().getAge());
System.out.println("--------------------");
p2.getChildren().setName("children2");//修改新对象中的Children引用
p2.getChildren().setAge(17);
//打印原对象中的内容
System.out.println(p.getName()+":"+p.getAge());
System.out.println(p.getChildren().getName()+":"+p.getChildren().getAge());
//打印新对象中修改了Children的内容
System.out.println(p2.getName()+":"+p2.getAge());
System.out.println(p2.getChildren().getName()+":"+p2.getChildren().getAge());
}
}
class Person implements Cloneable//实现Cloneable接口
{
int age;
String name;
Children children;
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 Children getChildren()
{
return children;
}
public void setChildren(Children children)
{
this.children = children;
}
@Override
public Object clone() throws CloneNotSupportedException//访问权限修改成public
{
return super.clone();
}
}
class Children
{
int age;
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;
}
}
结果:
lpp:28
children1:16
--------------------
lpp:28
children2:17
lpp:28
children2:17
可见,修改了新对象中的Chiledren引用所指向的内容,原对象和新对象中Children的内容都会改变,这就证实了浅克隆中,对象中对其他对象的引用所指向的内容没有被复制,始终只有一份。
【深克隆】
对上述代码做如下改变:
class Person implements Cloneable//实现Cloneable接口
{
int age;
String name;
Children children;
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 Children getChildren()
{
return children;
}
public void setChildren(Children children)
{
this.children = children;
}
@Override
public Object clone() throws CloneNotSupportedException//访问权限修改成public
{
Person p = (Person)super.clone();
p.setChildren((Children)(p.getChildren().clone()));//克隆p中的Children
return p;
}
}
class Children implements Cloneable//实现Cloneable接口
{
int age;
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;
}
@Override
public Object clone() throws CloneNotSupportedException//访问权限修改成public
{
return super.clone();
}
}
结果:
lpp:28
children1:16
--------------------
lpp:28
children1:16
lpp:28
children2:17
可知,克隆了Person中的Children后,Person中的Children引用指向一个新的Children,因此修改新的Children,不会对原来的Children有影响。
另一种实现深克隆的方法为将要克隆的内容进行序列化,然后再通过反序列化,将缓存中的内容读回来,应该强调的是写在流里的是原对象的一个拷贝,原对象还保存在JVM里面。这样做的前提是对象及所引用的对象都是可序列化的,否则应考虑将不可序列化的对象设为用transien,将其排除在序列化之外。
【利用序列化实现深克隆】
package com.lpp.clone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SerializableClone
{
public static void main(String[] args) throws Exception
{
Student s = new Student();
s.setAge(12);
s.setName("student1");
Teacher t = new Teacher();
t.setAge(28);
t.setName("lpp");
t.setStudent(s);
Teacher t2 = (Teacher)t.deepClone();
//打印新对象中的内容
System.out.println(t2.getName()+":"+t2.getAge());
System.out.println(t2.getStudent().getName()+":"+t2.getStudent().getAge());
System.out.println("--------------------");
t2.getStudent().setName("student2");//修改新对象中的Children引用
t2.getStudent().setAge(13);
//打印原对象中的内容
System.out.println(t.getName()+":"+t.getAge());
System.out.println(t.getStudent().getName()+":"+t.getStudent().getAge());
//打印新对象中修改了Children的内容
System.out.println(t2.getName()+":"+t2.getAge());
System.out.println(t2.getStudent().getName()+":"+t2.getStudent().getAge());
}
}
class Teacher implements Serializable//实现Serializable接口而不是Cloneable接口
{
int age;
String name;
Student student;
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 Student getStudent()
{
return student;
}
public void setStudent(Student student)
{
this.student = student;
}
public Object deepClone() throws Exception
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);//把当前对象的拷贝写到流里,进行序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();//从当前流里读取对象拷贝,进行反序列化
}
}
class Student implements Serializable//实现Serializable接口而不是Cloneable接口
{
int age;
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;
}
}
结果:
lpp:28
student1:12
--------------------
lpp:28
student1:12
lpp:28
student2:13
可见通过序列化,实现了对象的深度拷贝。这是实现深克隆的最好的办法。