浅拷贝(浅克隆):
复制出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
深拷贝(深克隆):
复制出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
其实本质上的区别就是一个是指针的复制,一个是指针背后真正数据的复制。因为java弱化了指针的定义,所以很多人对这个不太了解,大家可以看一下指针的定义和使用,然后在理解这个就容易了。我也给大家画几个价值千金的图来说明一下。

就好比我们
Student s1 =new Student("张三");
Student s2 =new Student("李四");
s2 = s1;
s1.name = "王五";
System.out.println(s2.name);
结果输出的是:王五。

浅拷贝就是引用的复制,其实内存还是一个内存,改变两个的其中一个都是改变堆内存中的对象实体。
而深拷贝就是在堆内存中复制一份,再由被引用。所以两个引用就是完全独立的,更改是不会影响的。
深拷贝:
public class Student implements Cloneable{
private String name;
public Student( String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return (Student)super.clone();
}
}
Student s1 = new Student("张三");
Student s2 = new Student("李四");
//
try {
s2 = s1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
s1.name = "王五";
System.out.println(s2.name);
输出:张三。

需要注意的是:如果对象里面有其他的对象,每一个对象都需要调用实现Cloneable接口,重写clone();我们来看一下对象里面嵌套对象。
static class Student implements Cloneable {
String name;
Course course;
public Student(String name, Course course) {
this.name = name;
this.course = course;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
}
static class Course {
String course_name;
int count;
//
public Course(String course_name, int count) {
this.course_name = course_name;
this.count = count;
}
public String getCourse_name() {
return course_name;
}
public void setCourse_name(String course_name) {
this.course_name = course_name;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
main:
Student s1 = new Student("张三",new Course("语文",2));
Student s2 = null;
try {
s2 = s1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
s1.course.course_name = "英语";
System.out.println(s2.getCourse().getCourse_name() );
System.out.println("s2==s1 :"+(s2==s1 ));
System.out.println("s2.getCourse()==s1.getCourse() :"+(s2.getCourse()==s1.getCourse() ));
System.out.println("s2.getCourse().getCourse_name()==s1.getCourse().getCourse_name() :"+(s2.getCourse().getCourse_name()==s1.getCourse().getCourse_name() ));
输出:
英语
s2==s1 :false
s2.getCourse()==s1.getCourse() :true
s2.getCourse().getCourse_name()==s1.getCourse().getCourse_name() :true
还是画图说明一下吧。

那么我们让Course也实现clone
static class Course implements Cloneable{
String course_name;
int count;
//
public Course(String course_name, int count) {
this.course_name = course_name;
this.count = count;
}
public String getCourse_name() {
return course_name;
}
public void setCourse_name(String course_name) {
this.course_name = course_name;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
那结果又是什么呢?
输出:
英语
s2==s1 :false
s2.getCourse()==s1.getCourse() :true
s2.getCourse().getCourse_name()==s1.getCourse().getCourse_name() :true
结果可能出乎了大家的意料,为何呢》那是因为你没有真正调用Course的clone方法。
@Override
protected Student clone() throws CloneNotSupportedException {
Student s = (Student) super.clone();
s.course = (Course) this.getCourse().clone();
return s;
}
这样就OK了
输出:
语文
s2==s1 :false
s2.getCourse()==s1.getCourse() :false
s2.getCourse().getCourse_name()==s1.getCourse().getCourse_name() :false
最后我们看看这样在集合里是否生效
ArrayList<Student> students = new ArrayList<>();
ArrayList<Student> students_copy = new ArrayList<>();
Student s11 = new Student("张三1", new Course("语文1", 1));
Student s12 = new Student("张三2", new Course("语文2", 2));
Student s13 = new Student("张三3", new Course("语文3", 3));
students.add(s11);
students.add(s12);
students.add(s13);
students_copy.addAll(students);
for(int i = 0;i<students_copy.size();i++){
System.out.println("students_copy:"+students_copy.get(i).getCourse().getCourse_name());
System.out.println("students:"+students.get(i).getCourse().getCourse_name());
}
输出:
students_copy:语文1
students:语文1
students_copy:语文2
students:语文2
students_copy:语文3
students:语文3
看出来什么问题了吗?
是滴,我们没有Clone,改一下
ArrayList<Student> students = new ArrayList<>();
ArrayList<Student> students_copy = new ArrayList<>();
Student s11 = new Student("张三1", new Course("语文1", 1));
Student s12 = new Student("张三2", new Course("语文2", 2));
Student s13 = new Student("张三3", new Course("语文3", 3));
students.add(s11);
students.add(s12);
students.add(s13);
for(int i = 0;i<students.size();i++){
students_copy.add(students.get(i).clone());
students.get(i).getCourse().setCourse_name("英语"+i);
}
for(int i = 0;i<students_copy.size();i++){
System.out.println("students_copy:"+students_copy.get(i).getCourse().getCourse_name());
System.out.println("students:"+students.get(i).getCourse().getCourse_name());
}
输出:
students_copy:语文1
students:英语0
students_copy:语文2
students:英语1
students_copy:语文3
students:英语2
也是生效的,很多资料里之所以说集合里面的不生效,大概是没有完全clone();但是我们也看到了这样做实在是麻烦,特别是 对象十分大而且复杂,这样克隆很容易出错误,怎么办呢?
public static <T> List<T> deepCopy(List<T> src) {
List<T> dest = null;
try {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(src);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream in = new ObjectInputStream(byteIn);
dest = (List<T>) in.readObject();
} catch (IOException e) {
} catch (ClassNotFoundException e) {
}
return dest;
}
通过IO流是个不错的方法,没有了上面的麻烦操作,但是记得实现Serializable接口哈。
其实还有一件事,就是网上的文章良莠不齐,很多都是copy,没有验证,所以大家在查资料的时候一定要亲自试一试,当然我可能有很多错误,同时希望可以得到大家指正。
网友评论