Java的深拷贝与浅拷贝的几种实现方式

Java的深拷贝和浅拷贝都是用于复制对象的常用方式。在进行对象复制时,我们需要了解两者的区别,根据具体需求选择合适的方式进行对象复制。

什么是浅拷贝?

浅拷贝是指直接复制对象,复制后的对象和原对象共享同一块内存空间(即,原对象和复制后对象都指向同一块内存空间)。如果对象存在引用类型属性,复制后的对象和原对象的引用类型属性指向同一个对象。如果其中一个对象对引用类型属性进行修改,另一个对象也会受到影响。

浅拷贝的三种实现方式

  1. 重写Object类的clone()方法

Java中的所有类都继承自Object类,该类中定义了一个clone()方法,用于创建一个新对象的副本。在使用该方法时,需要注意以下几点:

  • 类实现Cloneable接口,否则在调用clone方法时会抛出CloneNotSupportedException异常。
  • clone()方法返回的是Object类型,需要强制转换为需要的类型。
  • clone()方法是浅拷贝,如果需要实现深拷贝,则需要在重写clone()方法时进行手动处理。

示例代码:

public class Person implements Cloneable {
    private String name;
    private Address address;
    // get/set方法省略

    @Override
    public Object clone() {
        try {
            Person person = (Person)super.clone();
            person.address = (Address)address.clone();
            return person;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}

public class Address implements Cloneable {
    private String city;
    private String street;
    // get/set方法省略

    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}

// 测试代码
Person person1 = new Person();
person1.setName("张三");
Address address = new Address();
address.setCity("上海");
address.setStreet("浦东新区");
person1.setAddress(address);
Person person2 = (Person)person1.clone();
person2.getAddress().setCity("北京");
System.out.println(person1.getAddress().getCity()); //输出北京
  1. 使用对象的构造函数

在构造函数中,通过一个现有的对象来初始化另一个对象。这种方法也是一种浅拷贝,即如果原对象和复制对象都引用同一个对象,那么修改该共享对象时,原对象和复制对象都会受到影响。

示例代码:

public class Person {
    private String name;
    private Address address;
    // 构造函数
    public Person(Person person) {
        this.name = person.getName();
        this.address = person.getAddress();
    }
    // get/set方法省略
}

public class Address {
    private String city;
    private String street;
    // 构造函数
    public Address(Address address) {
        this.city = address.getCity();
        this.street = address.getStreet();
    }
    // get/set方法省略
}

// 测试代码
Person person1 = new Person();
person1.setName("张三");
Address address = new Address();
address.setCity("上海");
address.setStreet("浦东新区");
person1.setAddress(address);
Person person2 = new Person(person1);
person2.getAddress().setCity("北京");
System.out.println(person1.getAddress().getCity()); //输出北京
  1. 使用Java Bean的属性访问器

Java中,通过属性访问器来访问对象的属性值是一种常见的方式。使用该方式可以实现浅拷贝的效果。

示例代码:

public class Person {
    private String name;
    private Address address;
    // get/set方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
}

public class Address {
    private String city;
    private String street;
    // get/set方法
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
}

// 测试代码
Person person1 = new Person();
person1.setName("张三");
Address address = new Address();
address.setCity("上海");
address.setStreet("浦东新区");
person1.setAddress(address);
Person person2 = new Person();
person2.setName(person1.getName());
person2.setAddress(person1.getAddress());
person2.getAddress().setCity("北京");
System.out.println(person1.getAddress().getCity()); //输出北京

什么是深拷贝?

深拷贝是指复制对象并创建一个新的对象,新对象和原对象互不影响,如果对象存在引用类型属性,会复制出一个新的对象来。如果其中一个对象对引用类型属性进行修改,另一个对象不会受到影响。

深拷贝的两种实现方式

  1. 通过序列化和反序列化实现深拷贝

在Java中,ObjectOutputStream和ObjectInputStream类可以将对象序列化为字节流,对字节流进行反序列化可以将对象还原回来。通过这种方式可以实现深拷贝的效果。

示例代码:

public class Person implements Serializable {
    private String name;
    private Address address;
    // get/set方法省略
}

public class Address implements Serializable {
    private String city;
    private String street;
    // get/set方法省略
}

// 测试代码
Person person1 = new Person();
person1.setName("张三");
Address address = new Address();
address.setCity("上海");
address.setStreet("浦东新区");
person1.setAddress(address);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(person1);
out.flush();
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
Person person2 = (Person)in.readObject();
person2.getAddress().setCity("北京");
System.out.println(person1.getAddress().getCity()); //输出上海
  1. 通过递归拷贝实现深拷贝

该方式只能用于对象中没有循环引用的场景。其细节在于通过递归方式遍历对象的所有属性,并对属性值进行拷贝,从而实现深拷贝的效果。

示例代码:

public class Person {
    private String name;
    private Address address;
    // 构造函数
    public Person(Person person) {
        this.name = person.getName();
        this.address = new Address(person.getAddress());
    }
    // get/set方法省略
}

public class Address {
    private String city;
    private String street;
    // 构造函数
    public Address(Address address) {
        this.city = address.getCity();
        this.street = address.getStreet();
    }
    // get/set方法省略
}

// 测试代码
Person person1 = new Person();
person1.setName("张三");
Address address = new Address();
address.setCity("上海");
address.setStreet("浦东新区");
person1.setAddress(address);
Person person2 = new Person(person1);
person2.getAddress().setCity("北京");
System.out.println(person1.getAddress().getCity()); //输出上海

总结

本文详细介绍了Java的深拷贝和浅拷贝的定义、区别及其实现方式,并提供了多个示例程序帮助读者更好的理解浅拷贝和深拷贝。在选择使用浅拷贝或深拷贝时,需要考虑对象之间的关系、应用场景和性能等方面,根据具体情况选择合适的实现方式。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java的深拷贝与浅拷贝的几种实现方式 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • C++深入探究重载重写覆盖的区别

    C++深入探究重载、重写、覆盖的区别 在C++中,有三种不同的函数使用方法:重载(Overloading)、重写(Overriding)和覆盖(Hiding)。虽然它们有些相似之处,但它们各自有不同的用途和行为。以下是它们的详细解释。 重载(Overloading) 重载是指定义多个具有相同名称(函数名)但不同参数列表(参数类型、参数个数或参数顺序)的函数。…

    other 2023年6月26日
    00
  • python的tqdm模块的使用

    以下是关于“Python的tqdm模块的使用”的完整攻略,包括基本概念、安装、使用方法和示例。 基本概念 tqdm是Python中一个进度条库,可以在循环中显示进度条,方便用户了解程序的运行进度。它可以用于各种类型的循环,如for循环、while循环等。 安装 tqdm可以通过pip命令进行安装,如下所示: pip install tqdm 使用方法 使用t…

    other 2023年5月7日
    00
  • win10开始菜单点击右键没反应如何解决 图解win10开始菜单右键点击没反应

    如果在使用Win10开始菜单时发现右键无法使用,不能打开右键菜单,那么下面的攻略可能会对你有所帮助。 1. 检查鼠标设置是否正确 在一些情况下,右键无法使用可能是由于鼠标设置问题导致的。解决此类问题的方法如下: 点击开始菜单并输入“鼠标设置”,然后点击“鼠标设置”应用程序。 确保你在“鼠标”选项卡下。在此处可以看到选项卡中的有关鼠标的设置。 确认“右键”按钮…

    other 2023年6月27日
    00
  • Redis配置文件详解

    当在Linux服务器上安装Redis之后,就需要为Redis配置文件进行一些必要的修改,以便让Redis按照我们需要的方式来运行。本篇文章将详细讲解Redis配置文件的各种参数及其作用。 Redis配置文件的路径 Redis配置文件默认存储在Redis的安装目录下,文件名为redis.conf,可以通过以下命令查找: $ find / -name redis…

    other 2023年6月25日
    00
  • 如何正确的进行网站入侵渗透测试

    如何正确的进行网站入侵渗透测试 环境准备 安装Kali Linux或其他Linux发行版 安装常用的渗透工具,如Burp Suite、Nmap、Metasploit、SQLMap等 准备一个合法的目标网站,并获得合法的授权进行测试 渗透测试准备 收集目标网站的相关信息,包括IP地址、端口、响应信息、网站架构等 分析目标网站的安全漏洞,如SQL注入、XSS注入…

    other 2023年6月27日
    00
  • windowsserver2016激活方法+密钥

    Windows Server 2016激活方法及密钥 Windows Server 2016是微软推出的一款服务器操作系统,它提供了许多新的功能和改进,如容器、虚拟化、存空间直接访问等。在使用Windows Server 2016时,需要进行激活才能获得完整功能。本文将介绍Windows Server 6的激活方法及密钥,同时提供两个示例说明。 激活方法 W…

    other 2023年5月7日
    00
  • mysql区间范围查询问题

    以下是“MySQL区间范围查询问题的完整攻略”的标准markdown格式文本,其中包含两个示例: MySQL区间范围查询问题的解决方法 MySQL中,我们经常需要进行区间范围查询,例如查询某个时间段内的数据、查询某个价格区间内的商品等。但是,在进行区间范围查询时,我们需要注意一些问题,以避免查询结果不准确或者查询效率低下。以下是MySQL区间范围查询问题的解…

    other 2023年5月10日
    00
  • css外部样式加载Link与import的区别

    CSS外部样式加载Link与import的区别: CSS样式可以通过三种方式来加载和使用,分别是:内联方式、嵌入式和外部式。在外部式中,有两种方式:link和import。这两种方式都可以在HTML文档中引用外部CSS样式文件,但是它们有一些不同之处。下面就来详细讲解一下两种方式各自的优缺点以及使用时需要注意的事项。 1.Link标签 Link标签是HTML…

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部