Java对象的浅拷贝

news2024/11/29 16:26:51

介绍

Java对象的浅拷贝(Shallow Copy)是指创建一个新的对象,并复制原始对象的所有非静态字段到新对象。如果字段是基本类型,那么复制的就是基本类型的值。如果字段是引用类型,那么复制的就是引用,而不是引用的对象本身。也就是说,如果原始对象中包含其他对象的引用,那么拷贝后新对象的引用与原始对象引用相同,指向同一个对象。这就是为什么称之为“浅”拷贝,因为它只复制了对象的引用,而没有实际创建新的对象。

需要注意的地方

  • 浅拷贝只复制对象的引用,而不是对象本身。因此,如果原始对象中包含其他对象的引用,那么拷贝后新对象的引用与原始对象引用相同,指向同一个对象。
  • 浅拷贝可能会导致一些意外的结果。比如,如果原始对象中包含一个数组,那么拷贝后新对象的数组与原始对象的数组共享相同的元素引用。如果原始对象中的数组被修改,那么拷贝后的新对象也会被影响。
  • 在进行浅拷贝时,如果原始对象中包含不可变对象(如字符串或数字),那么拷贝后的新对象与原始对象共享这些不可变对象。但是,如果原始对象中包含可变对象(如数组或集合),那么拷贝后的新对象与原始对象共享这些可变对象的引用,因此需要对这些可变对象进行深拷贝或者重新创建。

在使用Java对象的浅拷贝时,需要特别注意浅拷贝的特性以及可能出现的意外情况,以确保程序的正确性和稳定性。

应用实例

通过clone方法实现

public class Person {
    private String name;
    private int age;
    private Address address;
    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}
public class Address {
    private String street;
    private String city;
    private String state;
    private String zip;
    public Address(String street, String city, String state, String zip) {
        this.street = street;
        this.city = city;
        this.state = state;
        this.zip = zip;
    }
}

在上面的示例中,Person类包含了一个Address对象的引用。为了实现浅拷贝,我们可以通过调用Object类的clone方法来创建一个新的Person对象,并将原始对象的所有非静态字段复制到新对象。在这个例子中,我们没有重写clone方法,因此默认实现是进行浅拷贝

Person person = new Person("John", 30, new Address("123 Main St", "Anytown", "CA", "12345"));
Person copyPerson = (Person) person.clone(); // 浅拷贝创建新对象并复制属性

通过手动复制的方式

除了使用Object.clone()方法实现浅拷贝,还可以通过手动复制每个字段来实现。例如:

public class Person {
    private String name;
    private int age;
    private Address address;
    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public Person shallowCopy() {
        return new Person(name, age, address);
    }
}

这种方式是通过创建一个新的Person对象,并将原始对象的所有非静态字段复制到新对象。这种方式比使用Object.clone()方法更直观一些,因为你可以清晰地看到复制的字段。例如:


Person person = new Person("John", 30, new Address("123 Main St", "Anytown", "CA", "12345"));
Person copyPerson = person.shallowCopy(); // 浅拷贝创建新对象并复制属性

通过stream流来实现

还可以使用Java 8的流(Stream)来实现浅拷贝。这种方式比较简洁,适合处理复杂对象结构。下面是一个示例:

public class Person {
    private String name;
    private int age;
    private Address address;
    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public Person shallowCopyUsingStreams() {
        return Stream.of(this)
                     .map(person -> person.name, person -> person.age, person -> person.address)
                     .collect(Collectors.toList())
                     .stream()
                     .map(this::new)
                     .collect(Collectors.toList())
                     .get(0);
    }
}

这种方式使用了Java 8的流API,通过将原始对象拆解为单个字段,再重新组装成新的对象来实现浅拷贝。例如:

Person person = new Person("John", 30, new Address("123 Main St", "Anytown", "CA", "12345"));
Person copyPerson = person.shallowCopyUsingStreams(); // 使用流API实现浅拷贝

通过反射来实现

还可以使用反射(Reflection)来实现浅拷贝。这种方式比较底层,可以处理任何类型的字段,包括私有字段。下面是一个示例:


import java.lang.reflect.Field;
public class Person {
    private String name;
    private int age;
    private Address address;
    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public Person shallowCopyUsingReflection() throws IllegalAccessException {
        Person newPerson = new Person(null, 0, null); // 创建一个新的Person对象
        Field[] fields = this.getClass().getDeclaredFields(); // 获取所有字段
        for (Field field : fields) {
            field.setAccessible(true); // 设置字段为可访问
            field.set(newPerson, field.get(this)); // 将原对象的字段值复制到新对象
        }
        return newPerson;
    }
}

这种方式使用了Java的反射API,通过直接操作字段来实现浅拷贝。例如:


Person person = new Person("John", 30, new Address("123 Main St", "Anytown", "CA", "12345"));
Person copyPerson = person.shallowCopyUsingReflection(); // 使用反射实现浅拷贝

函数式实现

以下是一个使用Java 8的java.util.function接口实现的浅拷贝方式。这种方式使用函数式编程,比较简洁。


import java.util.function.Function;
public class Person {
    private String name;
    private int age;
    private Address address;
    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public Person shallowCopyUsingFunction() {
        return Function.identity().andThen(Person::new).apply(name, age, address);
    }
}

这种方式使用了Function.identity()方法返回一个始终返回其输入参数的函数,再结合apply()方法,将输入参数作为新的Person对象的初始化参数。例如:


Person person = new Person("John", 30, new Address("123 Main St", "Anytown", "CA", "12345"));
Person copyPerson = person.shallowCopyUsingFunction(); // 使用函数式编程实现浅拷贝

通过序列化

以下是一个使用序列化的方式实现浅拷贝。这种方式比较简单,但需要将类标记为可序列化,并且需要考虑线程安全问题。


import java.io.*;
public class Person implements Serializable {
    private String name;
    private int age;
    private Address address;
    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public Person shallowCopyUsingSerialization() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        out.writeObject(this);
        out.flush();
        out.close();
        bos.close();
        byte[] bytes = bos.toByteArray();
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        return (Person) new ObjectInputStream(bis).readObject();
    }
}

这种方式将对象序列化为字节数组,然后通过反序列化创建浅拷贝对象。例如:


Person person = new Person("John", 30, new Address("123 Main St", "Anytown", "CA", "12345"));
Person copyPerson = person.shallowCopyUsingSerialization(); // 使用序列化实现浅拷贝

最佳实现

在Java中,对于对象的浅拷贝,最佳实践通常使用序列化(Serialization)或者使用拷贝构造器或者拷贝方法。
序列化是一种常见的实现方式,它可以将对象转换为字节流,然后再从字节流中恢复为对象。这种方式比较简单,但需要将类标记为可序列化,并且需要考虑线程安全问题。
拷贝构造器或拷贝方法是一种更直观的方式,可以在新的对象中复制原始对象的所有字段。这种方式需要手动实现每个字段的复制,但可以确保每个字段都被正确地复制。
无论哪种方式,都需要考虑对象的可变性、线程安全性和内存占用等问题。

以下是使用拷贝构造器和序列化实现浅拷贝的代码示例:

使用拷贝构造器实现浅拷贝:

public class Person {
    private String name;
    private int age;
    private Address address;
    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    // 拷贝构造器
    public Person(Person person) {
        this.name = person.name;
        this.age = person.age;
        this.address = new Address(person.address);
    }
}

使用序列化实现浅拷贝:

import java.io.*;
public class Person implements Serializable {
    private String name;
    private int age;
    private Address address;
    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    // 实现浅拷贝方法
    public Person shallowCopyUsingSerialization() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        out.writeObject(this);
        out.flush();
        out.close();
        bos.close();
        byte[] bytes = bos.toByteArray();
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        return (Person) new ObjectInputStream(bis).readObject();
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1187547.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

哪种小型洗衣机好用?迷你洗衣机品牌推荐

随着科技的快速发展,现在的人们越来越注重自己的卫生问题,不仅在吃上面会注重卫生问题,在用的上面也会更加严格要求,而衣服做为我们最贴身的东西,我们对它的要求也会更加高,所以最近这几年较火爆的无疑是内…

立体库堆垛机取货动作控制程序功能

取货动作功能块 为左出货台有货 DB11.DBX0.0 左出货台车就位 DB11.DBX0.2 右出货台车就位 DB11.DBX1.2 为右出货台有货 DB11.DBX1.0 左出货台车就位 DB11.DBX0.2 右出货台车就位 DB11.DBX1.2 T20上升保护时间

看这里!学习苹果手机更改锁屏密码的方法!

锁屏密码是用于保护设备与隐私安全的一项重要措施。用户设置锁屏密码后,在打开手机时需要输入正确的密码才能进入桌面。定期更改锁屏密码可以减少数据泄露的风险,那么,苹果手机更改锁屏密码?当您想要更改Apple设备的锁屏密码时&am…

createElement的用法

目录 一:介绍 二:语法与例子 1、语法 2、一些例子 例1: 例2: 例3: 3、第二种写法 一:介绍 document.createElement()是在对象中创建一个对象,要与appendChild() 或 insertBefore()方法…

js获取当前日期与7天后的日期

调用 console.log(this.getSectionData(7))结果 函数 getSectionData(section) {const now new Date()const nowYear now.getFullYear()const nowMonth now.getMonth() 1 < 10 ? (0 (now.getMonth() 1)) : (now.getMonth() 1)const nowDay now.getDate() < 1…

AJAX 入门笔记

课程地址 AJAX Asynchronous JavaScript and XML&#xff08;异步的 JavaScript 和 XML&#xff09; AJAX 不是新的编程语言&#xff0c;而是一种使用现有标准的新方法 AJAX 最大的优点是在不重新加载整个页面的情况下&#xff0c;可以与服务器交换数据并更新部分网页内容 XML…

GIS入门,xyz地图瓦片是什么,xyz数据格式详解,如何发布离线XYZ瓦片到nginx或者tomcat中

XYZ介绍 XYZ瓦片是一种在线地图数据格式,由goole公司开发。 与其他瓦片地图类似,XYZ瓦片将地图数据分解为一系列小的图像块,以提高地图显示效率和性能。 XYZ瓦片提供了一种开放的地图平台,使开发者可以轻松地将地图集成到自己的应用程序中。同时,它还提供了高分辨率图像和…

RT-Thread 11. Scons 选择不同的编译器编译BSP

1.使用gcc编译工程&#xff0c;生成elf、bin文件 如果是 ARM 平台的芯片&#xff0c;则可以使用 Env 工具&#xff0c;输入 scons 命令直接编译 BSP&#xff0c;这时候默认使用的是 ARM GCC 编译器&#xff0c;因为 Env 工具带有 ARM GCC 编译器。 2.使用template.uvproj 生成…

conda不同环境pip list包都一样问题;conda国内镜像加速

1、conda不同环境pip list包都一样问题 注意是因为conda创建不同环境里用pip安装容易导致&#xff0c;因为pip install安装 python包管理大多都默认到一个地方&#xff0c;正常用conda install就会有隔离 参考&#xff1a;https://blog.csdn.net/tywwwww/article/details/127…

【组件自定义事件+全局事件总线+消息订阅与发布+TodoList案例——编辑+过度与动画】

组件自定义事件全局事件总线消息订阅与发布TodoList案例——编辑过度与动画 1 组件自定义事件1.1 绑定1.2 解绑1.3 总结1.4 TodoList案例——自定义事件 2 全局事件总线2.1 理解2.2 步骤2.3 TodoList案例——事件总线 3 消息订阅与发布3.1 理解3.2 TodoList案例——消息的订阅与…

OpenText Voltage 数据隐私和保护解决方案

隐私从统一数据发现开始&#xff0c;通过 Voltage Fusion 了解您的数据&#xff0c;保护您最重要的资产&#xff0c;通过默认隐私安全使用数据。 优点 1、内置数据隐私 Voltage Fusion 平台使用人工智能快速识别风险。 2、支持有效行动的见解 为基于策略的行动分析和分类数据。…

分享66个Python管理系统源代码总有一个是你想要的

分享66个Python管理系统源代码总有一个是你想要的 源码下载链接&#xff1a;https://pan.baidu.com/s/1FGmE9Q_NE1-cjjoxU540BQ?pwd8888 提取码&#xff1a;8888 项目名称 automobile-sales-management-system汽车销售管理系统 Python Vue BNUZ教务系统认证爬虫Python语言…

云服务器玲琅满目的时代,为什么我独爱Amazon EC2 云服务器?

前言 作为一名程序员&#xff0c;我们不管什么时候都离不开一个云服务器&#xff0c;我们可以将网站部署在云服务器上&#xff0c;实现网站的访问和运行&#xff0c;如果你是学生&#xff0c;那么你可以清晰的向面试官去展示你的项目来提升你面试的成功率&#xff0c;也可以去…

电脑设置在哪里?5招快速进入设置!

“作为一名电脑新手&#xff0c;在操作电脑时真的感觉很多东西不会。我想调整一下我的电脑屏幕&#xff0c;需要进入设置中对某些参数进行调整&#xff0c;但是我不知道电脑设置在哪里。请大神帮帮我吧&#xff01;” 电脑设置可以帮助用户解决各种电脑问题&#xff0c;我们可以…

【Java】IntelliJ IDEA使用JDBC连接MySQL数据库并写入数据

目录 0 准备工作1 创建Java项目2 添加JDBC 驱动程序3 创建数据库连接配置文件4 创建一个 Java 类来连接和操作数据库5 运行应用程序 在 IntelliJ IDEA 中连接 MySQL 数据库并将数据存储在数据表中&#xff0c;使用 Java 和 JDBC&#xff08;Java Database Connectivity&#xf…

AI:71-基于深度学习的植物叶片识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

使用基于swagger的knife4j自动生成接口文档

添加swagger依赖springfox&#xff0c;添加knife4j依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId> </dependency> <dependency><groupId>com.github.xiaoymin</group…

在Windows 10上安装单机版的hadoop-3.3.5

1、Hadoop是一个由Apache基金会所开发的分布式系统基础架构。用户可以不需要了解分布式底层细节的情况下&#xff0c;开发分布式程序。充分利用集群进行高速运算和存储。 2、下载Hadoop&#xff0c;我们在清华大学的镜像站下载 Index of /apache/hadoop/core/hadoop-3.3.6 (t…

4、FFmpeg命令行操作1

一、环境搭建 1. 到ffmpeg官方下载已经编译好的Windows shared库&#xff1b; 2. 将执行文件ffmpeg.exe ffplay.exe ffprobe.exe拷贝到C:\Windows目录; 3. 将相应的动态库拷贝到C:\Windows\SysWOW64目录&#xff1b; 注&#xff1a;WOW64 (Windows-on-Windows 64-bit) 4.…