场景
现在有一个Student对象,想要将这个对象拷贝一份,有三种拷贝方式:
1.直接赋值给另一个变量,这样内存地址完全没变,没有实现拷贝。
Student student = new Student();
Student student1 = student;
2.浅拷贝(省略其余代码),只对Student 对象实现Cloneable接口,并实现clone方法。这样的结果是,Student对象实现了拷贝,但是Student内的其他对象,比如Student内部有一个成员Person对象就没有实现拷贝。
3.深拷贝,就是我们通常想要的完全拷贝,不仅该对象Student实现拷贝,该对象内部Person对象同样也实现了拷贝。深拷贝通常有两种方法,一种一层一层的实现Cloneable接口,还有一种是序列化方法。两者相比较,前者繁琐,但效率高,后者简便但效率低。
具体实现+测试
两个对象代码(省略无关get/set方法):
Person对象
public class Person implements Cloneable, Serializable {
private String name;
private int age;
private Integer age1;
@Override
protected Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
Student对象
public class Student implements Cloneable, Serializable {
private Person person;
private String className;
@Override
protected Student clone() {
try {
Student student = (Student) super.clone();
student.setPerson(this.getPerson().clone());
return student;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
1.直接赋值
/*1. 初始化一些值*/
Person person = new Person("张三", 20, 21);
Student student = new Student(person, "三年二班");
/*2. 直接拷贝的情况*/
/*
* 内存地址测试
* 打印结果为:
* student内存地址为:copy.Student@1540e19d
* studentCopy1内存地址为:copy.Student@1540e19d
* 结论:内存地址一致
* */
Student studentCopy1 = student;
System.out.println("student内存地址为:"+ student);
System.out.println("studentCopy1内存地址为:"+ studentCopy1);
2.浅拷贝
/*3. 浅拷贝测试*/
Student studentCopy2 = student.clone();
/*
* 内存地址测试
* 打印结果:
* student内存地址为:copy.Student@1540e19d
* studentCopy2内存地址为:copy.Student@677327b6
* student中Person内存地址为:copy.Person@14ae5a5
* studentCopy2中Person内存地址为:copy.Person@14ae5a5
* 结论:浅拷贝对于对象中的对象是拷贝不了的
* */
System.out.println("student内存地址为:"+ student);
System.out.println("studentCopy2内存地址为:"+ studentCopy2);
System.out.println("student中Person内存地址为:"+ student.getPerson());
System.out.println("studentCopy2中Person内存地址为:"+ studentCopy2.getPerson());
3.深拷贝
/*4. 深拷贝测试*/
long start = System.currentTimeMillis();
Student studentCopy3 = student.clone();
long end = System.currentTimeMillis();
/*
* 内存地址测试
* 打印结果:
* student内存地址为:copy.Student@1540e19d
* studentCopy3内存地址为:copy.Student@6d6f6e28
* student中Person内存地址为:copy.Person@14ae5a5
* studentCopy3中Person内存地址为:copy.Person@135fbaa4
* 耗时:0ms
* 结论:深拷贝确实可以做到所有对象拷贝,但是太麻烦,但性能极高
* */
System.out.println("student内存地址为:"+ student);
System.out.println("studentCopy3内存地址为:"+ studentCopy3);
System.out.println("student中Person内存地址为:"+ student.getPerson());
System.out.println("studentCopy3中Person内存地址为:"+ studentCopy3.getPerson());
System.out.println("耗时:" + (end - start) + "ms");
4.序列化实现深拷贝
/*5. 序列化实现深拷贝测试*/
start = System.currentTimeMillis();
Student studentCopy4 = clone(student); //序列化方法
end = System.currentTimeMillis();
/*
* 内存地址测试
* 打印结果:
* student内存地址为:copy.Student@1540e19d
* studentCopy4内存地址为:copy.Student@1d81eb93
* student中Person内存地址为:copy.Person@14ae5a5
* studentCopy4中Person内存地址为:copy.Person@7291c18f
* 耗时:85ms
* 结论:序列化实现深拷贝很方便,但是性能相比较前者慢很多
* */
System.out.println("student内存地址为:"+ student);
System.out.println("studentCopy4内存地址为:"+ studentCopy4);
System.out.println("student中Person内存地址为:"+ student.getPerson());
System.out.println("studentCopy4中Person内存地址为:"+ studentCopy4.getPerson());
System.out.println("耗时:" + (end - start) + "ms");
序列化实现的clone方法为:
public static <T extends Serializable> T clone(T obj){
T cloneObj = null;
try {
//写入字节流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();
//分配内存,写入原始对象,生成新对象
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
//返回生成的新对象
cloneObj = (T) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
结论
浅拷贝往往达不到我们要的效果,深拷贝才可以。
深拷贝有两种方式,无所谓耗时的采用实现Cloneable接口方式,考虑效率的采用序列化方式。
本文详细介绍了Java中对象拷贝的三种方式:直接赋值、浅拷贝和深拷贝。通过实例演示了不同拷贝方式下对象内存地址的变化,对比了深拷贝的两种实现方法:实现Cloneable接口与序列化。
472

被折叠的 条评论
为什么被折叠?



