1.首先理解下浅克隆和深克隆的含义吧:
浅克隆:简单说就是对当前要克隆的对象的复制,但是不会复制当前对象对其他对象的引用,即引用还是指向之前被引用对象的地址。
深克隆:就是把要克隆对象以及他所有引用对象全部复制一份。
2.浅克隆的代码演示:通过这段代码你就会明白浅克隆到底是什么意思了。
class Teacher{
public String name;
public int age;
public Teacher(String name,int age){
this.name = name;
this.age = age;
}
}
class Student implements Cloneable{
public String name;
public int age;
public Teacher teacher;
public Student(String name,int age,Teacher t){
this.name = name;
this.age = age;
this.teacher = t;
}
@Override
public Student clone() throws CloneNotSupportedException {
Student s= (Student)super.clone();
return s;
}
}
public class TestClone {
public static void main(String[] args) throws Exception {
Teacher teacher = new Teacher("Job",50);
Student stu1 = new Student("Kobe",24,teacher);
Student stu2 = stu1.clone();
System.out.println("stu1 == stu2?-->" + (stu1 == stu2));
System.out.println("stu1[name = "+ stu1.name + ",age = " + stu1.age
+ "],stu2[name = " + stu2.name + ",age = " + stu2.age + "]");
System.out.println("stu1.teacher == stu2.teacher?-->"
+ (stu1.teacher == stu2.teacher));
/**
* 此处改变stu2引用对象的值,会改变stu1引用对象的值
*
*/
stu2.teacher.name = "Mike";
stu2.teacher.age = 60;
System.out.println("stu1[name = " + stu1.teacher.name
+ ",age = " + stu1.teacher.age + "]");
}
}
由上段代码的结果可以看出:stu1和stu2的内存地址不同,只是形成了值得拷贝,Student对象中的Teacher对象的引用没有改变。一旦改变stu2引用Teacher的值,stu1所引用的Teacher的值也会改变,其实他俩指向的是同一内存地址,这就是所谓的浅克隆。
顺便说一下Object基类中clone()方法的含义:Object中的clone()方法可以识别出我们要复制的是哪一个对象,然后会为此对象分配内存空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
引用下官网API的一段话:
The method clone for class Object performs a specific cloning operation. First, if the class of this object does not implement the interface Cloneable, then a CloneNotSupportedException is thrown. Note that all arrays are considered to implement the interface Cloneable. Otherwise, this method creates a new instance of the class of this object and initializes all its fields with exactly the contents of the corresponding fields of this object, as if by assignment; the contents of the fields are not themselves cloned. Thus, this method performs a "shallow copy" of this object, not a "deep copy" operation.
3.深克隆代码的演示:
先来一段代码:
class Teacher implements Cloneable{
public String name;
public int age;
public Teacher(String name,int age){
this.name = name;
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student implements Cloneable{
public String name;
public int age;
public Teacher teacher;
public Student(String name,int age,Teacher t){
this.name = name;
this.age = age;
this.teacher = t;
}
@Override
public Student clone() throws CloneNotSupportedException {
Student s= (Student)super.clone();
s.teacher = (Teacher)teacher.clone();
return s;
}
}
public class TestClone {
public static void main(String[] args) throws Exception {
Teacher teacher = new Teacher("Job",50);
Student stu1 = new Student("Kobe",24,teacher);
Student stu2 = stu1.clone();
System.out.println("stu1 == stu2?-->" + (stu1 == stu2));
System.out.println("stu1[name = "+ stu1.name + ",age = " + stu1.age
+ "],stu2[name = " + stu2.name + ",age = " + stu2.age + "]");
System.out.println("stu1.teacher == stu2.teacher?-->"
+ (stu1.teacher == stu2.teacher));
/**
* 此处改变stu2引用对象的值,不会改变stu1引用对象的值,因为在Student的clone()方法中已经对引用Teacher进行了复制。
*
*/
stu2.teacher.name = "Mike";
stu2.teacher.age = 60;
System.out.println("stu1[name = " + stu1.teacher.name
+ ",age = " + stu1.teacher.age + "]");
}
}
从结果可以看出改变stu2引用对象属性的值,stu1中的引用对象属性值并没有改变,这在要拷贝对象中引用对象内部没有其他引用的时候是成立的,如果Teacher中有其他对象的引用,则这样又会产生浅克隆的现象,这样并不能叫做深克隆,除非一层层的拷贝下去,所以网上很多举例解释是错误的。
还有一种是通过串行化来进行深拷贝,也就是流的过程。把对象写入流--->串行化,从流中读出对象--->并行化,对象本身还是存在于JVM中,流也是对对象的一种拷贝。代码如下:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Teacher implements Serializable{
private static final long serialVersionUID = 1L;
public String name;
public int age;
public Teacher(String name,int age){
this.name = name;
this.age = age;
}
}
class Student implements Serializable{
private static final long serialVersionUID = 1L;
public String name;
public int age;
public Teacher teacher;
public Student(String name,int age,Teacher t){
this.name = name;
this.age = age;
this.teacher = t;
}
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());
}
}
public class TestClone {
public static void main(String[] args) throws Exception {
Teacher teacher = new Teacher("Job",50);
Student stu1 = new Student("Kobe",24,teacher);
Student stu2 = (Student)stu1.deepClone();
System.out.println("stu1 == stu2?-->" + (stu1 == stu2));
System.out.println("stu1[name = "+ stu1.name + ",age = " + stu1.age
+ "],stu2[name = " + stu2.name + ",age = " + stu2.age + "]");
System.out.println("stu1.teacher == stu2.teacher?-->"
+ (stu1.teacher == stu2.teacher));
/**
* 此处改变stu2引用对象的值,不会改变stu1引用对象的值
*
*/
stu2.teacher.name = "Mike";
stu2.teacher.age = 60;
System.out.println("stu1[name = " + stu1.teacher.name
+ ",age = " + stu1.teacher.age + "]");
}
}
从结果可以看出改变stu2引用对象属性的值,stu1中的引用对象属性值并没有改变,这种方法也可以实现深拷贝(前提是所有引用对象都必须可序列化,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将之排除在复制过程之外)
ok,深浅克隆到此结束,可以下班回家了,吼吼