java中三种拷贝方法举例总结

 更新时间:2024年09月12日 10:36:52   作者:小库抢板12  
在Java编程中,理解引用拷贝、浅拷贝和深拷贝对于对象复制和内存管理至关重要,这篇文章主要介绍了java中三种拷贝方法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在Java编程中,理解深拷贝(Deep Copy)、浅拷贝(Shallow Copy)和引用拷贝(Reference Copy)是非常重要的。这三种拷贝方式涉及对象复制和内存管理。以下是对它们的详细解释:

1. 引用拷贝(Reference Copy)

引用拷贝是最简单的一种拷贝方式。它只复制对象的引用,而不复制对象本身。换句话说,原始对象和新对象共享同一个内存地址。

Person person1 = new Person("Alice");
Person person2 = person1;  // 引用拷贝

person2.setName("Bob");

System.out.println(person1.getName()); // 输出 "Bob"
System.out.println(person2.getName()); // 输出 "Bob"

在上述代码中,person1person2指向同一个对象,所以改变其中一个对象的属性,另一个对象的属性也会被改变。

2. 浅拷贝(Shallow Copy)

浅拷贝会创建一个新对象,但新对象中的成员变量(如果是对象)仍然是原对象的引用。浅拷贝仅复制对象的第一层属性。

可以通过实现Cloneable接口并重写clone方法来实现浅拷贝:

class Address {
    String city;

    public Address(String city) {
        this.city = city;
    }
}

class Person implements Cloneable {
    String name;
    Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();  // 浅拷贝
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);
        Person person2 = (Person) person1.clone();

        person2.name = "Bob";
        person2.address.city = "Los Angeles";

        System.out.println(person1.name); // 输出 "Alice"
        System.out.println(person1.address.city); // 输出 "Los Angeles"
        System.out.println(person2.name); // 输出 "Bob"
        System.out.println(person2.address.city); // 输出 "Los Angeles"
    }
}

在上述代码中,person1person2拥有不同的name属性,但是共享同一个Address对象。

3. 深拷贝(Deep Copy)

深拷贝不仅创建一个新对象,还会递归地复制所有成员对象。这样,原对象和新对象完全独立,不共享任何引用。

深拷贝可以通过手动实现clone方法来完成,或者使用序列化。

手动实现深拷贝的示例:

class Address implements Cloneable {
    String city;

    public Address(String city) {
        this.city = city;
    }

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

class Person implements Cloneable {
    String name;
    Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = (Address) address.clone();  // 深拷贝
        return cloned;
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);
        Person person2 = (Person) person1.clone();

        person2.name = "Bob";
        person2.address.city = "Los Angeles";

        System.out.println(person1.name); // 输出 "Alice"
        System.out.println(person1.address.city); // 输出 "New York"
        System.out.println(person2.name); // 输出 "Bob"
        System.out.println(person2.address.city); // 输出 "Los Angeles"
    }
}

在上述代码中,person1person2的所有属性都是独立的,修改一个对象的属性不会影响另一个对象。
通过序列化和反序列化来实现Java的深拷贝是一种通用且方便的方法。它可以确保对象的完整复制,包括所有嵌套的成员对象。以下是具体的实现步骤:

  • 让类实现Serializable接口:确保需要深拷贝的类和它包含的所有成员类都实现Serializable接口。
  • 使用序列化和反序列化进行深拷贝:将对象写入字节流,然后从字节流中读出对象,从而实现对象的完全复制。

下面是一个具体的例子:

示例代码

import java.io.*;

class Address implements Serializable {
    private static final long serialVersionUID = 1L;
    String city;

    public Address(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Address{city='" + city + "'}";
    }
}

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', address=" + address + "}";
    }

    // 深拷贝方法
    public Person deepCopy() {
        try {
            // 序列化
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            oos.flush();
            oos.close();

            // 反序列化
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (Person) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);
        Person person2 = person1.deepCopy();

        person2.name = "Bob";
        person2.address.city = "Los Angeles";

        System.out.println("Original: " + person1);  // 原对象
        System.out.println("Copy: " + person2);  // 深拷贝后的对象
    }
}

代码解释

  • 实现Serializable接口:AddressPerson类都实现了Serializable接口,使它们可以被序列化。
  • 深拷贝方法deepCopy
    • 序列化:使用ObjectOutputStream将对象写入ByteArrayOutputStream
    • 反序列化:使用ObjectInputStreamByteArrayInputStream中读出对象,生成一个新的对象副本。
  • 测试深拷贝
    • 创建原始对象person1,并通过deepCopy方法生成person2
    • 修改person2的属性,验证原始对象person1不受影响,证明了对象的深拷贝。

这种方法的优点是实现简单,且适用于所有需要深拷贝的情况。然而,它也有一些限制,比如性能较慢(因为涉及IO操作)和必须实现Serializable接口。

附:深拷贝需要注意的点

在Java中,对象的深拷贝需要注意以下几点:

  • 不可拷贝对象的状态,只能拷贝对象的状态描述。Java中对象的状态包括实例变量和内部类实例等。
  • 对于可变对象,深拷贝后修改原始对象中的值也会修改拷贝对象中的值。因此,在进行深拷贝时需要创建新的对象,而不是直接引用原始对象。
  • 对于不可变对象,深拷贝后修改原始对象中的值不会影响拷贝对象中的值。
  • 对于数组和集合类对象,深拷贝时需要拷贝数组或集合中的每一个元素,而不仅仅是引用。
  • 对于非内存中的对象,例如文件、网络资源等,深拷贝时需要重新创建资源而不是简单地引用。
  • 对于非序列化的对象,深拷贝时需要通过实现Cloneable接口并重写clone()方法来实现。
  • 对于实现了Serializable接口的对象,可以通过序列化和反序列化来实现深拷贝。但是需要注意,如果对象中有不可序列化的字段,则无法通过序列化实现深拷贝。

总结

  • 引用拷贝:只复制引用,原对象和新对象指向同一个对象。
  • 浅拷贝:创建新对象,但不递归复制成员对象,成员对象仍然是共享的引用。
  • 深拷贝:创建新对象,并递归复制所有成员对象,完全独立。

这些拷贝方式在实际应用中有不同的使用场景和适用性,根据需要选择合适的拷贝方式可以有效管理内存和对象关系。

到此这篇关于java中三种拷贝方法的文章就介绍到这了,更多相关java拷贝方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java 10的10个新特性总结

    Java 10的10个新特性总结

    这篇文章我们给大家整理了关于Java 10的10个新特性以及相关要点内容,有兴趣的朋友们可以参考下。
    2018-08-08
  • springboot中的springSession的存储和获取实现

    springboot中的springSession的存储和获取实现

    这篇文章主要介绍了springboot中的springSession的存储和获取实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 一文带你了解SpringBoot中常用注解的原理和使用

    一文带你了解SpringBoot中常用注解的原理和使用

    这篇文章主要介绍了一文带你了解SpringBoot中常用注解的原理和使用
    2022-11-11
  • JSON各种转换问题(json转List,json转对象等)

    JSON各种转换问题(json转List,json转对象等)

    这篇文章主要介绍了JSON各种转换问题(json转List,json转对象等),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • java数据结构ArrayList详解

    java数据结构ArrayList详解

    本文详细讲解了java数据结构ArrayList的用法,文中通过示例代码介绍的非常详细。对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • Spring Boot中优雅地处理参数传递的技巧分享

    Spring Boot中优雅地处理参数传递的技巧分享

    最近一直在学习Spring Boot,今天将其中的从前台过来的参数传递总结一下,下面这篇文章主要给大家介绍了关于Spring Boot中优雅地处理参数传递的技巧,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • springboot如何通过URL方式访问外部资源

    springboot如何通过URL方式访问外部资源

    这篇文章主要介绍了springboot如何通过URL方式访问外部资源,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 使用通过ARP类似P2P终结者实现数据封包

    使用通过ARP类似P2P终结者实现数据封包

    目前网络上类似P2P终结者这类软件,主要都是基于ARP欺骗实现的,网络上到处都有关于ARP的介绍,不过为了本文读者不需要再去查找,我就在这里大概讲解一下
    2012-12-12
  • Springboot项目如何兼容老的Spring项目问题

    Springboot项目如何兼容老的Spring项目问题

    这篇文章主要介绍了Springboot项目如何兼容老的Spring项目问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • Java实现接口的枚举类示例

    Java实现接口的枚举类示例

    这篇文章主要介绍了Java实现接口的枚举类,结合实例形式分析了java接口的枚举类相关原理与使用技巧,需要的朋友可以参考下
    2019-08-08

最新评论