浅拷贝,深拷贝

本文详细解析了浅拷贝与深拷贝的概念,通过具体实例对比两者在对象复制过程中的区别,特别是对引用类型处理的不同。并提供了解决浅拷贝中引用类型同步修改问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

浅拷贝

浅拷贝结果是:新对像中包含的引用对象和原对象中包含的引用对象是同一个东东, 改变其中任何一个所包含的引用对象, 另一个也会受到影响

深拷贝

深拷贝结果是:新对象与原对象是完全独立的, 改变其中任何一个所包含的引用对像, 另一个都不会受到影响

 

浅拷贝例子  Object中的clone默认是浅拷贝

1 实现cloneable接口,这是个标记接口

2 重写Object的clone方法,调用父类的clone方法

public class Student {
    private String name; // 姓名
    private int age; // 年龄
    private StringBuffer sex; // 性别

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public StringBuffer getSex() {
        return sex;
    }

    public void setSex(StringBuffer sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }
}
public class School implements Cloneable {
    private String schoolName; // 学校名称
    private int stuNums; // 学校人数
    private Student stu; // 一个学生

    public String getSchoolName() {
        return schoolName;
    }

    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }

    public int getStuNums() {
        return stuNums;
    }

    public void setStuNums(int stuNums) {
        this.stuNums = stuNums;
    }

    public Student getStu() {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu;
    }

    
    
    @Override
    protected School clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        return (School)  super.clone();
    }

    
    @Override
    public String toString() {
        return "School [schoolName=" + schoolName + ", stuNums=" + stuNums + ", stu=" + stu + "]";
    }
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
        School s1 = new School();
        s1.setSchoolName("实验小学");
        s1.setStuNums(100);
        Student stu1 = new Student();
        stu1.setAge(20);
        stu1.setName("zhangsan");
        stu1.setSex(new StringBuffer(""));
        s1.setStu(stu1);
        System.out.println(    "s1: " + s1 + " s1的hashcode:" + s1.hashCode() + "  s1中stu1的hashcode:" + s1.getStu().hashCode());
        School s2 = s1.clone(); // 调用重写的clone方法,clone出一个新的school---s2
        System.out.println("s2: " + s2 + " s2的hashcode:" + s2.hashCode() + " s2中stu1的hashcode:" + s2.getStu().hashCode());

    }
}

s1和s2是不同的对象,但是他们包含的学生是同一个对象

s1: School [schoolName=实验小学, stuNums=100, stu=Student [name=zhangsan, age=20, sex=男]] s1的hashcode:366712642 s1中stu1的hashcode:1829164700
s2: School [schoolName=实验小学, stuNums=100, stu=Student [name=zhangsan, age=20, sex=男]] s2的hashcode:2018699554 s2中stu1的hashcode:1829164700

 

深拷贝

1 学生类实现cloneable接口

2 学生类重写clone方法

3 修改学校类的clone方法

public class Student implements Cloneable {

    private String name;
    private int age;
    private StringBuffer sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public StringBuffer getSex() {
        return sex;
    }

    public void setSex(StringBuffer sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }

    @Override
    protected Student clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        return (Student) super.clone();
    }
}
public class School implements Cloneable {
    private String schoolName; // 学校名称
    private int stuNums; // 学校人数
    private Student stu; // 一个学生

    public String getSchoolName() {
        return schoolName;
    }

    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }

    public int getStuNums() {
        return stuNums;
    }

    public void setStuNums(int stuNums) {
        this.stuNums = stuNums;
    }

    public Student getStu() {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu;
    }

    @Override
    protected School clone() throws CloneNotSupportedException {
        School s = null;
        s = (School) super.clone();
        s.stu = stu.clone();
        return s;
    }

    @Override
    public String toString() {
        return "School [schoolName=" + schoolName + ", stuNums=" + stuNums + ", stu=" + stu + "]";
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        School s1 = new School();
        s1.setSchoolName("实验小学");
        s1.setStuNums(100);
        Student stu1 = new Student();
        stu1.setAge(20);
        stu1.setName("zhangsan");
        stu1.setSex(new StringBuffer(""));
        s1.setStu(stu1);
        System.out.println("s1: " + s1 + " s1的hashcode:" + s1.hashCode() + "  s1中stu1的hashcode:" + s1.getStu().hashCode());
        School s2 = s1.clone(); // 调用重写的clone方法,clone出一个新的school---s2
        System.out.println("s2: " + s2 + " s2的hashcode:" + s2.hashCode() + " s2中stu1的hashcode:" + s2.getStu().hashCode());

        // 修改s2中的值,看看是否会对s1中的值造成影响
        s2.setSchoolName("希望小学");
        s2.setStuNums(200);
        Student stu2 = s2.getStu();
        stu2.setAge(30);
        stu2.setName("lisi");
        stu2.setSex(stu2.getSex().append("6666666"));
        s2.setStu(stu2);

        // 再次打印两个school,查看结果
        System.out.println("-------------------------------------------------------------------------");
        System.out.println("s1: " + s1 + " hashcode:" + s1.hashCode() + "  s1中stu1的hashcode:" + s1.getStu().hashCode());
        System.out.println("s2: " + s2 + " hashcode:" + s2.hashCode() + " s2中stu1的hashcode:" + s2.getStu().hashCode());

    }
}

 

s1: School [schoolName=实验小学, stuNums=100, stu=Student [name=zhangsan, age=20, sex=男]] s1的hashcode:366712642 s1中stu1的hashcode:1829164700
s2: School [schoolName=实验小学, stuNums=100, stu=Student [name=zhangsan, age=20, sex=男]] s2的hashcode:2018699554 s2中stu1的hashcode:1311053135
-------------------------------------------------------------------------
s1: School [schoolName=实验小学, stuNums=100, stu=Student [name=zhangsan, age=20, sex=男6666666]] hashcode:366712642 s1中stu1的hashcode:1829164700
s2: School [schoolName=希望小学, stuNums=200, stu=Student [name=lisi, age=30, sex=男6666666]] hashcode:2018699554 s2中stu1的hashcode:1311053135

 

可以修改s2中的性别,s1的也发生了改变,原因在于sex的类型是Stringbuffer,属于引用类型,在clone的时候将StringBuffer对象的地址传递了过去

这种情况应该怎么解决呢?

修改学生类的clone方法,设置sex时创建一个新的StringBuffer对象

 



转载于:https://www.cnblogs.com/moris5013/p/10697701.html

### Java 中浅拷贝深拷贝的区别及用法 #### 1. 浅拷贝 (Shallow Copy) 浅拷贝是指创建一个新的对象,这个新对象会复制原对象中的基本数据类型字段值以及引用类型的内存地址。这意味着,在浅拷贝的情况下,如果原始对象包含其他对象的引用,则这些引用会被简单地复制到新的对象中,而不是重新创建被引用的对象。 在 Java 中可以通过 `Object` 类的 `clone()` 方法来实现浅拷贝[^1]。需要注意的是,默认情况下,`clone()` 只能完成浅拷贝操作。下面是一个浅拷贝的例子: ```java class Person implements Cloneable { String name; int age; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public class Main { public static void main(String[] args) throws CloneNotSupportedException { Person original = new Person(); original.name = "Alice"; original.age = 25; Person copied = (Person) original.clone(); System.out.println(copied.name); // 输出 Alice System.out.println(copied.age); // 输出 25 } } ``` 上述代码展示了如何通过重写 `clone()` 方法来进行浅拷贝[^4]。 #### 2. 深拷贝 (Deep Copy) 深拷贝不仅复制了对象本身,还递归地复制了该对象所引用的所有子对象。因此,即使修改了原始对象的内容,也不会影响到副本对象的状态。这使得深拷贝更加安全但也更耗费资源和时间。 要实现深拷贝,可以采用序列化的方式或者手动逐级克隆每一个成员变量及其内部结构[^3]。以下是利用序列化的例子: ```java import java.io.*; class Address implements Serializable { String city; } class Employee implements Serializable, Cloneable { String name; transient Address address; private Object readResolve() throws ObjectStreamException { this.address = new Address(); this.address.city = "New York"; return this; } @Override protected Object clone() throws CloneNotSupportedException { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } catch (IOException | ClassNotFoundException e) { throw new RuntimeException(e); } } } public class DeepCopyExample { public static void main(String[] args) throws Exception{ Employee empOriginal = new Employee(); empOriginal.name = "John Doe"; Address addr = new Address(); addr.city = "Los Angeles"; empOriginal.address = addr; Employee deepCopiedEmp = (Employee)empOriginal.clone(); System.out.println(empOriginal.name.equals(deepCopiedEmp.name)); // true System.out.println(empOriginal.address == deepCopiedEmp.address); // false } } ``` 此示例说明了如何借助序列化机制执行真正的深拷贝过程。 #### 3. 主要差异对比 | 特性 | 浅拷贝 | 深拷贝 | |-------------------|----------------------------------|--------------------------------| | **定义** | 复制基础数据类型并共享引用 | 完全独立的新实例 | | **性能开销** | 较低 | 高 | | **适用场景** | 数据量较小或无需完全隔离的情况 | 要求严格分离的数据处理 | #### 4. 总结 当仅需快速复制少量数据而不关心后续更改时可选用浅拷贝;而对于那些需要保持绝对一致性的复杂数据结构则应考虑使用深拷贝
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值