Java深浅拷贝

文章目录

目录

文章目录

前言

1) 引用拷贝

2) 对象拷贝

二 . 浅拷贝

1) 拷贝构造方法实现浅拷贝

2) 工厂方法实现浅拷贝

3) clone方法实现浅拷贝

三 . 深拷贝

1) 通过重写clone方法来实现深拷贝

2) 通过对象序列化实现深拷贝


前言

大家好,今天给大家带来的是java中的深浅拷贝


一 . 拷贝的引入

1) 引用拷贝

引用拷贝通常指的是浅拷贝,即只复制了对象本身,而没有复制对象内部的引用对象。这意味着原始对象和拷贝对象仍然共享相同的引用对象

创建一个指向对象的引用变量的拷贝对象

Student s1 = new Student("张三",23);
Student s2 = s1;
System.out.println(s1);
System.out.println(s2);

2) 对象拷贝

创建对象本身的一个副本。

Student student = new Student("张三",23);
Student otherStudent = (Student)student.clone();
System.out.println(student);
System.out.println(otherStudent);

结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。

Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去。

在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用现有对象的部分或全部 数据。

Java中的对象拷贝主要分为:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。

二 . 浅拷贝

浅拷贝是指在拷贝对象时,只复制对象本身,而不复制对象内部的引用对象。这意味着原始对象和拷贝对象会共享相同的引用对象,如果引用对象发生改变,原始对象和拷贝对象都会受到影响。

①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。

②对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。

在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

浅拷贝的实现方式主要有三种

1) 拷贝构造方法实现浅拷贝

class Person {
    Age age;
    String name;

    public Person(Age age, String name) {
        this.age = age;
        this.name = name;
    }

    /**
     * 拷贝构造方法
     * @param p
     */
    public Person(Person p){
        this.age = p.age;
        this.name = p.name;
    }

}

class Age{
    int age;

    public Age(int age) {
        this.age = age;
    }
}
Age age = new Age(18);
Person person1 = new Person(age,"张三");
Person person2 = new Person(person1);
System.out.println(person1);
System.out.println(person2);

 

2) 工厂方法实现浅拷贝

改变一个方法就行,其他的不变

    /**
     * 工厂方法
     * @param p
     */
    public Person clone(Person p){
        return new Person(p.age,p.name);
    }

3) clone方法实现浅拷贝

Object类是类结构的根类,其中有一个方法为protected Object clone() throws CloneNotSupportedException,这个方法就是进行的浅拷贝。有了这个浅拷贝模板,我们可以通过调用clone()方法来实现对象的浅拷贝。

但是需要注意:

1、Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以我们无法直接使用。

2、使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。

对于这两点,我们的解决方法是,在要使用clone方法的类中重写clone()方法,通过super.clone()调用Object类中的原clone方法

public class ShallowCopy {
    public static void main(String[] args) throws CloneNotSupportedException {
        Age age = new Age(18);
        Person person1 = new Person(age,"张三");
        Person person2 = (Person)person1.clone();
        System.out.println(person1);
        System.out.println(person2);
    }
}

class Person implements Cloneable{
    Age age;
    String name;

    public Person(Age age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Age{
    int age;

    public Age(int age) {
        this.age = age;
    }
}

三 . 深拷贝

深拷贝指的是在复制对象时,不仅复制了对象本身,还复制了对象内部的所有引用对象,使得原始对象和拷贝对象完全独立,彼此不会相互影响。

在进行深拷贝时,需要递归地复制对象及其内部的所有引用对象,确保每个对象都是独立的

浅拷贝示意图

深拷贝示意图

如果Age对象依然关联其他引用对象,同样也是需要拷贝的

深拷贝的实现方法主要有两种

1) 通过重写clone方法来实现深拷贝

class Animal implements Cloneable{
    int age;
    String name;
    Dog dog;

    public Animal(int age, String name, Dog dog) {
        this.age = age;
        this.name = name;
        this.dog = dog;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Animal animal = (Animal)super.clone();
        animal.setDog((Dog)animal.getDog().clone());
        return animal;
    }
}

class Dog implements Cloneable{
    int age;
    String name;

    public Dog(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
Animal animal = new Animal(1,"小黄",new Dog(1,"小黄"));
Animal cloneAnimal = (Animal) animal.clone();
System.out.println(animal);
System.out.println(cloneAnimal);
System.out.println(animal.getDog());
System.out.println(cloneAnimal.getDog());

结果分析 : 两个引用animal和cloneAnimal指向不同的两个对象,两个引用animal和cloneAnimal中的两个Dog引用指向的是两个对象,但对Dog对象的修改只能影响animal对象,所以说是深拷贝。

2) 通过对象序列化实现深拷贝

虽然层次调用clone方法可以实现深拷贝,但是显然代码量实在太大。特别对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。

将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。

参考代码如下

import java.io.*;

class MyClass implements Serializable {
    int number;

    public MyClass(int number) {
        this.number = number;
    }
}

public class DeepCopyExample {
    public static void main(String[] args) {
        // 创建一个原始对象
        MyClass originalObject = new MyClass(10);

        // 使用序列化和反序列化进行深拷贝
        MyClass copiedObject = deepCopy(originalObject);

        // 修改原始对象的值
        originalObject.number = 20;

        // 打印拷贝对象的值,验证是否进行了深拷贝
        System.out.println(copiedObject.number);  // 输出 10
    }

    // 使用序列化和反序列化进行深拷贝
    public static <T extends Serializable> T deepCopy(T object) {
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream output = new ObjectOutputStream(outputStream);
            output.writeObject(object);

            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream input = new ObjectInputStream(inputStream);
            return (T) input.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

 总结

这篇文章这要给大家介绍了一下java中的深浅拷贝,我们下一篇博客见

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢吃animal milk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值