Java浅拷贝和深拷贝的区别
程序员文章站
2024-03-05 18:31:31
...
浅拷贝和深拷贝的区别
浅拷贝和深拷贝都是复制对象,复制出来的对象,它们的内存地址都是重新分配的,区别在于浅拷贝对象中的引用类型和原对象中的引用类型指向同一个内存地址,而深拷贝对象中的引用类型的内存地址是重新分配的,也就是说,浅拷贝对象和原对象的引用类型的数据是同步的,深拷贝对象和原对象的引用类型的数据是互不干扰的。
注意: 这里说的是引用类型!对于对象中直接定义的基本数据类型及其包装类型、String
类型这些数据类型,由于原对象和拷贝对象的内存地址是重新分配的,因此这些数据的改变不会影响到另一个对象。
注意: 再强调一次,请区分对象中的基本数据类型及其包装类型、String
类型,这些不属于引用类型!特别要注意的是,浅拷贝中,对象中的引用类型的地址是同一个内存地址,引用类型中的基本数据类型及其包装类型、String
类型的改变会同步到所有浅拷贝对象及原对象中!
浅拷贝的实现
- 方法一:类要实现
Cloneable
接口,重写Object
的clone
方法,在clone
方法中调用super.clone()
方法即可。 - 方法二:只要能实现一个对象的所有属性都拷贝到另一个新对象的属性中即可,通常使用方法一
public class ShallowCopy implements Cloneable {
public ShallowCopy() {
}
public ShallowCopy(int age, Integer count, String name, Bean bean) {
this.age = age;
this.count = count;
this.name = name;
this.bean = bean;
}
private int age;
private Integer count;
private String name;
private Bean bean;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "ShallowCopy hash is " + this.hashCode() + " ,{" + "name=" + name + ", count=" + count + ", age='" + age + '\'' + ", bean=" + bean + "}";
}
}
深拷贝的实现
深拷贝有两种实现方式
- 方法一:所有自定义类都要实现
Cloneable
接口,重写Object
的clone
方法,在对象的clone
方法中先调用父类的clone
方法生成当前类的新对象,然后调用各个引用类型的clone
方法生成各个引用类型的新对象,最后把所有引用类型的对象设置到当前类的新对象中。 - 方法二:通过对象序列化实现深拷贝。所有引用类型都要实现
Serializable
接口,各个属性要有setter
和getter
方法,通过ByteArrayOutputStream、ObjectOutputStream、ByteArrayInputStream和ObjectInputStream
完成对象的拷贝。
public class DeepCopy implements Serializable {
public DeepCopy() {
}
public DeepCopy(int age, Integer count, String name, Bean bean) {
this.age = age;
this.count = count;
this.name = name;
this.bean = bean;
}
private int age;
private Integer count;
private String name;
private Bean bean;
@Override
public String toString() {
return "DeepCopy hash is " + this.hashCode() + " ,{" + "age=" + age + ", count=" + count + ", name='" + name + '\'' + ", bean=" + bean + '}';
}
}
上述代码涉及到的类,所有类的setter和getter都省略了
public class Bean implements Serializable {
public Bean() {
}
public Bean(String clazz, int x, Integer y) {
this.clazz = clazz;
this.x = x;
this.y = y;
}
private String clazz;
private int x;
private Integer y;
@Override
public String toString() {
return "Bean hash is " + this.hashCode() + " ,{" + "clazz='" + clazz + '\'' + ", x=" + x + ", y=" + y + '}';
}
}
public class Main {
public static void main(String[] args) throws Exception {
print("浅拷贝");
shallow();
print();
print("深拷贝");
deep();
}
public static void print(String title) {
System.out.println("*********************" + title + "*********************");
}
public static void print() {
System.out.println("");
}
public static void deep() throws Exception {
DeepCopy A = new DeepCopy(18, 99999, "A", new Bean("三年级", 0, 99999));
System.out.println(A.toString());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(A);
oos.flush();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
DeepCopy B = (DeepCopy) ois.readObject();
System.out.println(B.toString());
}
public static void shallow() throws Exception {
ShallowCopy A = new ShallowCopy(18, 99999, "A", new Bean("三年级", 0, 99999));
print("观察 A 和 B 对象的 hash");
System.out.println(A.toString());
ShallowCopy B = (ShallowCopy) A.clone();
System.out.println(B.toString());
print();
print("修改B对象的基本数据类型/String类型,观察 A 和 B 对象的 hash");
B.setAge(1);
B.setCount(1);
B.setName("B");
System.out.println(A.toString());
System.out.println(B.toString());
print();
print("修改C对象的引用类型中的数据,观察 A 和 C 对象的 hash");
ShallowCopy C = (ShallowCopy) A.clone();
Bean bean = C.getBean();
bean.setX(1111111);
System.out.println(A.toString());
System.out.println(C.toString());
}
}
测试结果
注意hash值的变化,上述代码中测试了几种情况
- 修改
B
对象的基本数据类型/包装类型/String类型,观察A
和B
对象的 hash和数据的变化 - 修改
C
对象的引用类型中的数据,观察A
和C
对象的hash
和数据的变化
上一篇: 简单谈谈PHP中的Reload操作