关于Java的浅拷贝和深拷贝
2020-07-23 本文已影响0人
西敏寺钟声
浅拷贝和深拷贝是什么?
浅拷贝和深拷贝都是针对已经存在了的对象的操作,在java中,基本数据类型有八种,和引用数据类型。在程序中,一般用 =
来做赋值的操作,对于基本数据类型,实际上是拷贝它的值,而对于引用数据类型,则是拷贝的它的引用地址,举例如下:
@Test
public void m2() {
int i = 1;
int j = i;
System.out.println(j);
Student stu1 = new Student("zhangsan", 12);
System.out.println(stu1);
Student stu2 = stu1;
stu2.setAge(20);
System.out.println(stu1);
System.out.println(stu2);
}
运行结果:
1
Student{name='zhangsan', age=12, active=null}
Student{name='zhangsan', age=20, active=null}
Student{name='zhangsan', age=20, active=null}
从运行的结果,发现一个问题,int类型的值不用多说,而在引用类型中,明明修改的是stu2的age,为什么stu1的age也发生了变化,这里就涉及到了值传递和引用传递的问题了,ok,后面介绍。
Java中的clone()方法
说到拷贝操作,那java必定提供了API来供我们使用,那就是Object类中的clone()方法了(它是一个native方法),既然是超类中的protected方法,那么子类中就有必要重写一下,所有使用调用clone()来拷贝的时候,其对象必须要实现标识接口Cloneable,否则就会抛出CloneNotSupportedException这个异常。
浅拷贝
首先创建一个Studen类,并实现Cloneable接口,重写clone方法。
public class Student implements Cloneable {
private String name;
private int age;
private Active active;
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
// 省略get、set方法
}
Active类的实现:
public class Active implements Cloneable {
private String name;
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Active(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试用例:用clone方法,复制新new出来的student1,得到student2,再做各种比较。
@Test
public void m1() throws Exception {
Student student1 = new Student();
student1.setAge(10);
student1.setName("zhangsan");
student1.setActive(new Active("pingpang"));
// 原始对象的hashcode
System.out.println(student1.hashCode());
// 克隆出来的对象
Student student2 = (Student) student1.clone();
// 克隆出来的对象的hashcode
System.out.println(student2.hashCode());
// 两个对象是否一样
System.out.println(student1 == student2);
// 两个对象比较
System.out.println(student1);
System.out.println(student2);
// 两个对象各自的引用比较
System.out.println(student1.getActive());
System.out.println(student2.getActive());
}
运行结果:
697960108
943010986
false
Student@299a06ac
Student@383534aa
Active@6bc168e5
Active@6bc168e5
可以得出关于浅拷贝的几点结论:
- clone()方法,会创建一个新的对象。
- 浅拷贝基本数据类型是拷贝值。
- 拷贝引用数据类型如:
Active
,则拷贝的是它的引用地址。
深拷贝
Student类,在克隆此类对象的时候,将此对象中的引用属性也克隆一份,只有clone方法不同:
public class Student implements Cloneable {
private String name;
private int age;
private Active active;
public Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.active = (Active) this.active.clone();
return student;
}
// get、set方法省略
}
Active类,实现Cloneable接口,并重写了clone方法:
public class Active implements Cloneable {
private String name;
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Active(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试用例:
@Test
public void m1() throws Exception {
Student student1 = new Student();
student1.setAge(10);
student1.setName("zhangsan");
student1.setActive(new Active("pingpang"));
// 原始对象的hashcode
System.out.println(student1.hashCode());
// 克隆出来的对象
Student student2 = (Student) student1.clone();
// 克隆出来的对象的hashcode
System.out.println(student2.hashCode());
// 两个对象是否一样
System.out.println(student1 == student2);
// 两个对象比较
System.out.println(student1);
System.out.println(student2);
// 两个对象各自的引用比较
System.out.println(student1.getActive());
System.out.println(student2.getActive());
}
测试结果中显示不同student对象中的active对象也不同了:
697960108
943010986
false
com.nmys.story.interview.copy_interview.Student@299a06ac
com.nmys.story.interview.copy_interview.Student@383534aa
com.nmys.story.interview.copy_interview.Active@6bc168e5
com.nmys.story.interview.copy_interview.Active@7b3300e5
可以得出深拷贝的几点结论:
- 深拷贝是完全创建一个新的对象
- 深拷贝也会将原始对象中的引用对象重新复制一份
总结
- 一定要实现Cloneable接口。
- 重写clone()方法,注意:默认是浅拷贝,这里需要将引用类型进行深拷贝处。
- 特殊:String类虽然是引用类型,但是是final类,同时也有字符串常量池的存在,不必进行处理。
- 深拷贝可以通过上面所说的clone方法实现,还可以通过序列化来实现。
- 当时写测试用例的时候,我这里用了lombok的注解@Data,为的就是不用手写get、set方法了,但测试的时候,发现clone出来的对象的hashcode是一样的,原因是lombok在为我们生成get、set的同时,toString、hashCode、equals等方法都替我们重写了,导致clone出来的类虽然
==
是false
,但是hashCode却一样,建议还是手动写比较好。 - 最后再总结下,假设,A 类中引用了 B 类,B 类中引用了 C 类,对象分别对应 a,b,c,浅拷贝 a 的结果 a1,b,c。深拷贝 a 的结果 a1,b1,c1。