Java 对象的浅拷贝和深拷贝

mac2025-10-17  6

一、概述

对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部数据。Java中有三种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。:

浅拷贝: 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。深拷贝:深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。无论是浅拷贝还是深拷贝,都需要实现 clone() 方法,来完成操作。

二、浅拷贝

public class TestDemo{ public static void main(String[] args) { Friend friend = new Friend("Ace","man",18); // 原始对象 Person person = new Person("Jeffrey",18, friend); System.out.println("原始对象: " + person); // 拷贝对象 Person clonedPperson = (Person)person.clone(); System.out.println("拷贝对象: " + clonedPperson); //原始对象和拷贝对象是否一样 System.out.println("原始对象和拷贝对象是否一样: " + (person == clonedPperson)); //修改基本类型 person.setAge(20); System.out.println("修改基本类型后的对比:"); System.out.println("原始对象: " + person); System.out.println("拷贝对象: " + clonedPperson); //修改引用类型 person.getFriend().setAge(30); System.out.println("修改引用类型后的对比:"); System.out.println("原始对象: " + person); System.out.println("拷贝对象: " + clonedPperson); } } class Person implements Cloneable{ private String name; private int age; private Friend friend; public Person(String name, int age ,Friend friend) { this.name = name; this.age = age; this.friend = friend; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public Friend getFriend() { return friend; } @Override public Object clone(){ try { return super.clone(); } catch (CloneNotSupportedException e) { return null ; } } public String toString() { return "Person={ " + "name=" + name + ", age=" + age + ", friend=" + friend+" }"; } } class Friend{ private String name; private String sex; private int age; public Friend(String name,String sex,int age) { this.name = name; this.sex = sex; this.age = age; } public void setSex(String sex) { this.sex = sex; } public void setAge(int age) { this.age = age; } public String toString() { return "{ Friend:" + "name=" + name + ", sex=" + sex + ", age=" + age + " }"; } }

输出结果:

原始对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } } 拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } } 原始对象和拷贝对象是否一样: false 修改基本类型后的对比: 原始对象: Person={ name=Jeffrey, age=20, friend={ Friend:name=Ace, sex=man, age=18 } } 拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } } 修改引用类型后的对比: 原始对象: Person={ name=Jeffrey, age=20, friend={ Friend:name=Ace, sex=man, age=30 } } 拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=30 } }

根据结果来看,通过Clonable接口并重写Object类的clone()方法来赋值的对象,值类型的赋值不会改变,引用类型会改变,印证了只是赋值栈上面的数据和引用对象地址,最终指向堆的内存地址一致。

三、深拷贝

常用的方案有两种:

继续利用 clone()方法,对其内的引用类型的变量,再进行一次clone()(Friend实现),或者在clone()创建新的引用变量赋值和新的对象序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象。

1、clone

修改Pesron类,新创建一个对象返回:

@Override public Object clone(){ // 深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立 return new Person(name, age,new Friend(friend.name,friend.sex,friend.age)); }

2、序列化实现

具体参考Java 序列化,只是把FileOutputStream改成ByteArrayOutputStream。因为主要是FileOutputStream作用于文件,后者作用于byte[]。

参考Java 深拷贝和浅拷贝和细说 Java 的深拷贝和浅拷贝

最新回复(0)