浅拷贝
在Java中我们对于基本数据类型变量的拷贝通常是重新复制一份或者是对对象的引用重新复制一份,这种拷贝方式是浅拷贝
深拷贝
有时候我们想拷贝整个对象的内容包括和这个对象相关联的对象的所有内容,这种拷贝是深拷贝
实现方式
通常实现的方法是实现使用Object中的clone方法。
protected native Object clone() throws CloneNotSupportedException;
因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。
要想对一个对象进行复制,就需要对clone方法覆盖。
实现步骤
被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)
注意此方法是进行的浅拷贝
public class Student implements Cloneable {
private String name;
private int age;
private String sex;
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
/**
* Getter method for property age.
*
* @return property value of age
*/
public int getAge() {
return age;
}
/**
* Setter method for property age
*
* @param age value to be assigned to property age
*/
public void setAge(int age) {
this.age = age;
}
/**
* Getter method for property sex.
*
* @return property value of sex
*/
public String getSex() {
return sex;
}
/**
* Setter method for property sex
*
* @param sex value to be assigned to property sex
*/
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student stu = (Student)super.clone();
return stu;
}
public static void main(String[] args) throws Exception {
Student stu1 = new Student();
stu1.setAge(10);
Student stu2 = (Student)stu1.clone();
stu2.setAge(20);
System.out.println(stu1);
System.out.println(stu2);
}
}
结果:
Student{name='null', age=10, sex='null'}
Student{name='null', age=20, sex='null'}
Process finished with exit code 0
在类 student中属性比较简单,如果有个属性是对象会怎么样呢?
public class Student implements Cloneable {
private String name;
private int age;
private String sex;
private MyTest myTest = new MyTest();
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
/**
* Getter method for property age.
*
* @return property value of age
*/
public int getAge() {
return age;
}
/**
* Setter method for property age
*
* @param age value to be assigned to property age
*/
public void setAge(int age) {
this.age = age;
}
/**
* Getter method for property sex.
*
* @return property value of sex
*/
public String getSex() {
return sex;
}
/**
* Setter method for property sex
*
* @param sex value to be assigned to property sex
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* Getter method for property myTest.
*
* @return property value of myTest
*/
public MyTest getMyTest() {
return myTest;
}
/**
* Setter method for property myTest
*
* @param myTest value to be assigned to property myTest
*/
public void setMyTest(MyTest myTest) {
this.myTest = myTest;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student stu = (Student)super.clone();
return stu;
}
public static void main(String[] args) throws Exception {
Student stu1 = new Student();
stu1.setAge(10);
stu1.getMyTest().setValue("stu1");
Student stu2 = (Student)stu1.clone();
stu2.setAge(20);
stu2.getMyTest().setValue("stu2");
System.out.println(stu1+"value = "+stu1.getMyTest().getValue());
System.out.println(stu2+"value = "+stu2.getMyTest().getValue());
}
}
结果是:
Student{name='null', age=10, sex='null'}value = stu2
Student{name='null', age=20, sex='null'}value = stu2
Process finished with exit code 0
对象Mytest的value值两个是同一个,我们发现当依赖有对象时,原对象的值和拷贝后的值是一样的,这是为什么呢?
这是刚开始提到的浅拷贝拷贝的是对象的引用,对象Mytest拷贝的是引用,引用指向的是同一个内存区域,所以修改一个两个都会生效
为了保证修改的准确性,就有使用深拷贝的方式
之前提到了Java序列化与反序列化的方式,深拷贝我们就可以使用到这个方法来实现,要序列化类务必要实现Serializable接口才行,代码如下
public class MyTest implements Serializable {
private String value;
/**
* Getter method for property value.
*
* @return property value of value
*/
public String getValue() {
return value;
}
/**
* Setter method for property value
*
* @param value value to be assigned to property value
*/
public void setValue(String value) {
this.value = value;
}
}
public class Student implements Cloneable,Serializable {
private String name;
private int age;
private String sex;
private MyTest myTest = new MyTest();
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
/**
* Getter method for property age.
*
* @return property value of age
*/
public int getAge() {
return age;
}
/**
* Setter method for property age
*
* @param age value to be assigned to property age
*/
public void setAge(int age) {
this.age = age;
}
/**
* Getter method for property sex.
*
* @return property value of sex
*/
public String getSex() {
return sex;
}
/**
* Setter method for property sex
*
* @param sex value to be assigned to property sex
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* Getter method for property myTest.
*
* @return property value of myTest
*/
public MyTest getMyTest() {
return myTest;
}
/**
* Setter method for property myTest
*
* @param myTest value to be assigned to property myTest
*/
public void setMyTest(MyTest myTest) {
this.myTest = myTest;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
/**
* 深拷贝
*
* @param obj
* @return
* @throws Exception
*/
public static <T>T objClone(T obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(obj);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream in = new ObjectInputStream(bais);
T retT = (T)in.readObject();
return retT;
}
public static void main(String[] args) throws Exception {
Student stu1 = new Student();
stu1.setAge(10);
stu1.getMyTest().setValue("stu1");
Student stu2 = Student.objClone(stu1);
stu2.setAge(20);
stu2.getMyTest().setValue("stu2");
System.out.println(stu1+"value = "+stu1.getMyTest().getValue());
System.out.println(stu2+"value = "+stu2.getMyTest().getValue());
}
}
结果
Student{name='null', age=10, sex='null'}value = stu1
Student{name='null', age=20, sex='null'}value = stu2
Process finished with exit code 0
即实现了对象的深拷贝
重点是这部分代码,通过泛型采用了对象序列化和反序列化的方式
/**
* 深拷贝
*
* @param obj
* @return
* @throws Exception
*/
public static <T>T objClone(T obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(obj);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream in = new ObjectInputStream(bais);
T retT = (T)in.readObject();
return retT;
}
总结:
浅拷贝是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝,没有对引用指向的对象进行拷贝。
而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝。
区别就在于是否对对象中的引用变量所指向的对象进行拷贝。