1.浅复制与深复制概念
(1)浅复制(浅克隆)被复制对象的所有变量都含有与原来对象相同的值,而所有的对其他对象的引用仍然只指向原来的对象,换言之,浅复制仅仅复制锁考虑的对象,而不复制它所引用的对象。(2)深复制(深克隆)被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量,那些引用其他对象的变量将指向被复制过的新对象,而不再试原有的那些被引用的对象,换言之,深复制把要复制的对象所引用的对象都复制了一遍。
实现ICloneable接口的方式取决于我们的类型的数据成员。如果类型仅包含值类型(int,byte等类型)和string类型的数据成员,我们只要在Clone方法中初始化一个新的对象,将其的数据成员设置为当前对象的各个成员的值即可。事实上,object类的MemberwiseClone方法会自动完成该过程。
如果自定义类型包含引用类型的数据成员,必须考虑Clone方法是实现浅拷贝(shallow copy)还是深拷贝(deep copy)。浅拷贝是指副本对象中的引用类型的数据成员与源对象的数据成员指向相同的对象。而如果是深拷贝,则必须创建整个对象的结构,副本对象中的引用类型的数据成员与源对象的数据成员指向不同的对象。
浅拷贝是容易实现的,就是使用前面提到的MemberwiseClone方法。开发人员往往希望使用的类型能够实现深拷贝,但会发现这样的类型并不多。这种情况在System.Collections命名空间中尤其常见,这里面的类在其Clone方法中实现的都是浅拷贝
2. java的clone()方法
(1)clone方法将对象复制了一份并返回给调用之,一般而言,clone()方法满足:
对任何的对象C=X,都有x.clone()!=x//克隆对象与源对象不是同一个对象
对任何的对象X,都有x.clone().getClass()==x.getClass()//克隆对象与源对象类型一样
如果对象X的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
(2)java对象中的克隆
为了获取对象的一份拷贝,我们可以利用Object类得clone()方法。
在派生类中覆盖基类的clone(),并声明为public。
在派生类的clone()方法中,调用super.clone().
在派生类中实现Cloneable接口
代码如下
package clone;
public class Student implements Cloneable{
String name;
int age;
Student(String name,int age){
this.name=name;
this.age=age;
}
public Object clone(){
Object o =null;
try{
o=(Student)super.clone();//Object中的clone()识别出你要复制的哪一个对象
}
catch(CloneNotSupportedException e){
System.out.println(e.toString());
}
return o;
}
public static void main(String[] args){
Student s1 = new Student("zhang",18);
Student s2 = (Student)s1.clone();
s2.name="li";
s2.age=20;
System.out.println("name="+s1.name+","+"age="+s1.age);//修改学生2后不影响学生1的值
}
}
说明:
1问什么我们在派生类中覆盖Object的clone方式时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
继承自java.lang.Object类得clone()方式是浅复制,以下代码可以证明
此处浅拷贝的测试代码如果要运行正确需要把professor类中的o.p=(Professor)p.clone();去掉
package clone;
public class CloneTestPro
{
public static void main(String[] args)
{
Professor p = new Professor("王教授",50);
Student s1 = new Student("张学生",18,p);
System.out.println("s1:" + s1.name + " " + s1.age + " " + s1.p.name+ " " + s1.p.age);
//拷贝一个学生
Student s2 = (Student)s1.clone();
s2.age=21;
s2.name="WUDI";
System.out.println("s2:" + s2.name + " " + s2.age + " "+ s2.p.name+ " " + s2.p.age);
System.out.println("after s2 changing some property:");
//通过学生s2改变了他所在学校的名称和成立时间
s2.name="Tianjin University";
s2.age=12;
System.out.println("s1:" + s1.name + " " + s1.age + " " + s1.p.name+ " " + s1.p.age);
System.out.println("s2:" + s2.name + " " + s2.age + " " + s2.p.name+ " " + s2.p.age);
//从运行结果可以看出,通过学生s2将其引用school所指向的对象的name和bornYear属性改变,
//相应的s1的也被改变,这说明学生s1和学生s2里面的school引用指向的始终是同一个School对象。这就是浅拷贝的含义。
}
}
2.那应该如何实现深层次的克隆,即修改s2的教授不会影响s1的教授?代码改进如下。
(1)浅拷贝
默认的拷贝都是浅拷贝,你可以通过下面的方法实现:
class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException
{
return super.clone();
}
}
class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException
{
return super.clone();
}
}
浅拷贝需要记住的是原对象的各属性最好都是基本类型或者不可变的对象。
(2)深拷贝
深拷贝是相对浅拷贝的,解决不能管理子对象的问题的。
实现方法如下:
class Employee implements Cloneable
{
public Object clone() throws CloneNotSupportedException
{
Employee cloned = (Employee)super.clone();
//深拷贝子对象
cloned.dep = (Department)dep.clone();
return cloned.
}
}
值得注意的是 :如果需要使用clone方法,必需实现java.lang.Cloneable接口,否则会抛出java.lang.CloneNotSupportedException。
另外clone方法所做的的操作是直接复制字段的内容,换句话说,这个操作并不管该字段对应的对象实例内容。
像这样字段对字段的拷贝(field to field copy)就成为"浅拷贝",clone方法所做的正是"浅拷贝"。