一、概念
引用拷贝:直接复制对象的引用。如下代码,copy是ex对象的引用拷贝,那么copy和ex是同一个引用,指向的也是同一个对象,那么自然拷贝对象和原对象的属性也是同一个。
@Data
public Class Example {
private String str;
public static void main() {
Example ex = new Example;
Example copy = ex;
System.out.println(ex == copy); // true
System.out.println(ex.str == copy.str); // true
}
}
浅拷贝:浅拷贝可以通过实现Coleable接口实现。浅拷贝会创建一个新对象,但是对象内的属性需要注意:
(1) 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据;
(2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个字符串、数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
@Date
@AllArgsConstructor
public class Example implements Cloneable {
private int a;
private String b;
@Override
public Example clone() {
try {
return (Example) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public static void main() {
Example ex = new Example(1, "old_data");
Example copy = ex.clone(); // 浅拷贝
System.out.println(ex == copy) // false 因为对象拷贝会创建一个新的对象
System.out.println(ex.getB() == copy.getB()) // true 对象类型的成员属性传递的对象的引用
ex.setA(10);
System.out.println(ex.getA()); //10
System.out.println(copy.getA()); // 1 浅拷贝原始类型复制一份新数据给新的对象,不互相影响
ex.setB("new_data");
System.out.println(ex.getB()); // new_data
System.out.println(copy.getB()); // new_data 浅拷贝引用类型,就是原对象的成员变量的引用
}
}
深拷贝:深拷贝不仅会创建一个新的对象,对象中的对象类型的成员属性也会创建一个新的,而不是引用原对象的成员。可改写一下浅拷贝中重写的clone()方法即可实现。
@Data
public class AnotherClass implements Cloneable {
private String str;
@Override
public AnotherClass clone() {
return (AnotherClass) super.clone();
}
}
@Data
public class Example implements Cloneable {
private int a;
private AnotherClass b;
public Example(int a, String str) {
this.a = a;
this.b.setStr(str);
}
public void setStr(String str) {
this.b.setStr(str);
}
@Override
public Example clone() {
try {
Example ret = (Example) super.clone();
ret.setB(this.b.clone());
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public static void main() {
Example ex = new Example(1, "old_data");
Example copy = ex.clone(); // 深拷贝
System.out.println(ex == copy) // false 因为对象拷贝会创建一个新的对象
System.out.println(ex.getB() == copy.getB()) // false 对象类型的成员变量也是一个新对象
ex.setA(10);
System.out.println(ex.getA()); //10
System.out.println(copy.getA()); // 1 浅拷贝原始类型复制一份新数据给新的对象,不互相影响
ex.setStr("new_data");
System.out.println(ex.getB().getStr()); // new_data
System.out.println(copy.getB().getStr()); // old_data 深拷贝引用类型是一个新的对象
}
}
补充说明:上述通过重写clone()方法实现深拷贝的方式需要成员对象也去实现cloneable接口,特别对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。更好的实现方式是对象序列化实现。
@Data
@AllArgsConstructor
public class Example implements Serializable {
private int a;
private String b;
public static void main(String[] args) {
Example ex = new Example(1, "old_data");
//通过序列化方法实现深拷贝
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos= null;
try {
oos = new ObjectOutputStream(bos);
oos.writeObject(ex);
oos.flush();
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Example copy = (Example) ois.readObject();
copy.setB("new_data");
System.out.println("ex.getB():" + ex.getB()); // old_data
System.out.println("copy.getB():" + copy.getB()); // new_data
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}