java 对象的克隆(浅克隆和深克隆)

Java 对象的克隆指的是创建一个与原始对象相同的新对象,但两个对象的引用地址是不同的。根据克隆的深度不同,可以分为浅克隆和深克隆两种。

浅克隆

浅克隆是指在克隆一个对象时,只复制对象中的基本类型数据和对象的引用地址,而不是复制对象中引用对象的内容。这意味着,克隆后的对象和原始对象共享引用对象,即对其中一个对象的更改会对另一个对象产生影响。

如何进行浅克隆

Java 中使用clone()方法来实现对象的浅克隆。需要注意,被克隆的对象必须实现 Cloneable 接口,并且重写 clone() 方法。示例如下:

public class MyObject implements Cloneable {
    private int id;
    private String name;
    private Date createTime;

    public MyObject(int id, String name, Date createTime) {
        this.id = id;
        this.name = name;
        this.createTime = createTime;
    }

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

    public static void main(String[] args) throws CloneNotSupportedException {
        MyObject obj1 = new MyObject(1, "Object1", new Date());
        MyObject obj2 = (MyObject) obj1.clone();

        System.out.println("obj1:" + obj1);
        System.out.println("obj2:" + obj2);
        System.out.println("obj1 == obj2:" + (obj1 == obj2)); // false
    }
}

以上示例中,MyObject 类实现了 Cloneable 接口,并重写了 clone() 方法。在 main() 方法中,创建了一个原始对象 obj1,然后通过调用 clone() 方法创建 obj2。输出结果表明,obj1 和 obj2 的引用地址不同,即克隆是成功的。

浅克隆的示例说明

示例还是以上面的 MyObject 类为例。在浅克隆中,obj1 和 obj2 中的 createTime 属性存储的是同一个 Date 对象的引用,即两个对象共享一个 Date 对象,因此如果修改 obj1 中的 createTime 属性,则 obj2 中的 createTime 属性也会受到影响。示例如下:

public static void main(String[] args) throws CloneNotSupportedException {
    MyObject obj1 = new MyObject(1, "Object1", new Date());
    MyObject obj2 = (MyObject) obj1.clone();

    obj1.getCreateTime().setTime(11111111); // 修改 createTime 属性

    System.out.println("obj1:" + obj1);
    System.out.println("obj2:" + obj2);
}

输出结果如下:

obj1:MyObject(id=1, name=Object1, createTime=Wed Jan 01 08:53:31 CST 1970)
obj2:MyObject(id=1, name=Object1, createTime=Wed Jan 01 08:53:31 CST 1970)

可以看出,obj2 中的 createTime 属性也被修改了。

深克隆

深克隆是指在克隆一个对象时,不仅要复制对象中的基本类型数据和对象的引用地址,还要复制对象中引用对象的内容。这意味着,克隆后的对象和原始对象不共享引用对象,即对其中一个对象的更改不会对另一个对象产生影响。

如何进行深克隆

Java 实现深克隆的方式有很多,这里介绍其中两种方式:序列化和递归克隆。

序列化方式

序列化是将一个 Java 对象转化为字节流数据的过程。可以通过将对象序列化为字节流数据,再将字节流数据反序列化为新的对象来实现深克隆。示例如下:

public class MyObject implements Serializable {
    private int id;
    private String name;
    private Date createTime;

    public MyObject(int id, String name, Date createTime) {
        this.id = id;
        this.name = name;
        this.createTime = createTime;
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        MyObject obj1 = new MyObject(1, "Object1", new Date());
        MyObject obj2 = (MyObject) ObjectUtils.deepClone(obj1);

        obj1.getCreateTime().setTime(11111111); // 修改 createTime 属性

        System.out.println("obj1:" + obj1);
        System.out.println("obj2:" + obj2);
    }
}

public class ObjectUtils {
    // 深克隆方法
    public static Object deepClone(Object obj) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        return ois.readObject();
    }
}

在以上示例中,MyObject 类实现了 Serializable 接口。ObjectUtils 类中定义了一个静态的 deepClone() 方法,该方法接收一个对象作为参数,并返回一个与参数对象相同的新对象。在 main() 方法中,创建了一个原始对象 obj1,然后通过调用 deepClone() 方法创建了一个新的对象 obj2。输出结果表明,obj1 和 obj2 的引用地址不同且 createTime 属性不共享引用对象,即深度克隆成功。

递归克隆方式

递归克隆方式是指在进行深克隆的过程中,对于对象中的引用类型属性,递归调用克隆方法,直到将所有的引用对象都克隆出来。示例如下:

public class MyObject implements Cloneable {
    private int id;
    private String name;
    private Date createTime;
    private List<String> tags;

    public MyObject(int id, String name, Date createTime, List<String> tags) {
        this.id = id;
        this.name = name;
        this.createTime = createTime;
        this.tags = tags;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        MyObject obj = (MyObject) super.clone();
        obj.createTime = (Date) this.createTime.clone();
        obj.tags = new ArrayList<>(this.tags);
        return obj;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        MyObject obj1 = new MyObject(1, "Object1", new Date(), Arrays.asList("tag1", "tag2"));
        MyObject obj2 = (MyObject) obj1.clone();

        obj1.getCreateTime().setTime(11111111); // 修改 createTime 属性
        obj1.getTags().add("tag3"); // 修改 tags 属性

        System.out.println("obj1:" + obj1);
        System.out.println("obj2:" + obj2);
    }
}

在以上示例中,MyObject 类中的 clone() 方法首先调用了 super.clone() 方法克隆了基本类型属性和 tags 属性(因为 List 属性是引用类型),然后对 createTime 属性进行了单独的深克隆。在 main() 方法中,创建了一个原始对象 obj1,然后通过调用 clone() 方法创建了一个新的对象 obj2。输出结果表明,obj1 和 obj2 的引用地址不同且 createTime 属性不共享引用对象,即深度克隆成功。

深克隆的示例说明

示例还是以上面的 MyObject 类为例。在深克隆中,obj1 和 obj2 中的 createTime 属性存储的是两个不同的 Date 对象,即两个对象不共享一个 Date 对象,因此如果修改 obj1 中的 createTime 属性,则 obj2 中的 createTime 属性不会受到影响。同时,obj1 和 obj2 中的 tags 属性也存储的是不同的 List 对象,即两个对象不共享一个 List 对象,因此如果修改 obj1 中的 tags 属性,则 obj2 中的 tags 属性也不会受到影响。示例如下:

public static void main(String[] args) throws CloneNotSupportedException {
    MyObject obj1 = new MyObject(1, "Object1", new Date(), Arrays.asList("tag1", "tag2"));
    MyObject obj2 = (MyObject) obj1.clone();

    obj1.getCreateTime().setTime(11111111); // 修改 createTime 属性
    obj1.getTags().add("tag3"); // 修改 tags 属性

    System.out.println("obj1:" + obj1);
    System.out.println("obj2:" + obj2);
}

输出结果如下:

obj1:MyObject(id=1, name=Object1, createTime=Wed Jan 01 08:53:31 CST 1970, tags=[tag1, tag2, tag3])
obj2:MyObject(id=1, name=Object1, createTime=Thu Sep 17 19:38:31 CST 1970, tags=[tag1, tag2])

可以看出,obj2 中的 createTime 属性和 tags 属性没有被修改。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java 对象的克隆(浅克隆和深克隆) - Python技术站

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

相关文章

  • 详解JSONObject和JSONArray区别及基本用法

    详解JSONObject和JSONArray区别及基本用法 1. JSONObject和JSONArray是什么? 在Java中,JSONObject和JSONArray都是JSON格式数据的提供者。 JSONObject对象表示一个JSON对象,即类似于{ “name”: “张三”, “age”: 18, “gender”: “male” }这样的数据结构…

    Java 2023年5月26日
    00
  • 简介Java的Spring框架的体系结构以及安装配置

    下面我将详细讲解“简介Java的Spring框架的体系结构以及安装配置”的完整攻略。 1. 介绍 Spring框架是一款轻量级的开源Java框架,用于构建企业级应用程序。它提供了全方位的功能来支持开发大型、复杂的企业级应用程序。Spring框架由多个模块组成,每个模块负责提供不同的功能,每个模块都可以独立使用,也可以一起使用,非常灵活。 2. Spring框…

    Java 2023年5月19日
    00
  • springBoot中的properties配置解析

    在Spring Boot中,可以使用properties文件来配置应用程序的属性。这些属性可以用于配置数据源、日志、缓存、安全等方面。本文将详细讲解Spring Boot中的properties配置解析,包括如何定义属性、如何使用属性、如何覆盖属性等。 定义属性 在Spring Boot中,可以使用application.properties或applica…

    Java 2023年5月15日
    00
  • 二十分钟 教你Ruby快速入门 图文教程第1/4页

    下面进行对“二十分钟 教你Ruby快速入门 图文教程第1/4页”的完整攻略的详细讲解。 攻略内容 1. 环境搭建 首先,你需要在自己的电脑中安装 Ruby 程序,可以到 Ruby官网 下载相应的安装包,根据自己电脑的操作系统选择适合自己的安装程序。 安装完成后,可通过命令行工具输入 ruby -v 进行检查,若返回了相应版本号则说明安装成功。 2. Hell…

    Java 2023年5月26日
    00
  • spring scheduled单线程和多线程使用过程中的大坑

    介绍 在使用Spring框架的Scheduled定时任务功能时,我们可以通过配置ThreadPoolTaskExecutor来实现多线程执行定时任务。但是在使用过程中,可能会遇到一些坑,比如在多线程执行过程中,由于任务执行的时间过长,而ThreadPoolTaskExecutor线程池中可用线程数量过少,可能造成任务阻塞,从而导致已经执行的任务无法被及时释放…

    Java 2023年5月19日
    00
  • Tomcat中catalina.out 和 catalina.log的区别和用途详解

    题目:Tomcat中catalina.out 和 catalina.log的区别和用途详解 概述 在Tomcat的日志输出中,经常出现catalina.out和catalina.log两个常用的日志文件,本篇攻略主要介绍这两个日志文件的区别和用途。 区别 在Tomcat中,catalina.out和catalina.log两个日志文件的区别主要表现在以下几个…

    Java 2023年6月2日
    00
  • java实现微信小程序登录态维护的示例代码

    为了方便描述,本次攻略将会分为以下四个部分进行讲解: 微信小程序登录流程 前端获取微信小程序登录态并传递给后端 后端维护微信小程序登录态 示例代码说明 下面将逐一进行讲解。 1. 微信小程序登录流程 用户授权:用户通过微信小程序点击登录按钮,小程序调用wx.login()方法获取临时登录凭证code,并在回调函数中将code传递给小程序前端。 前端获取登录态…

    Java 2023年5月23日
    00
  • Java简单计算两个日期月数差的方法

    Java计算两个日期月数差的方法可以分为以下几个步骤: 将两个日期按照年、月、日拆分成年、月、日分别存储; 计算两个日期之间相差的总月数以及剩余天数; 根据剩余天数是否大于零进行判断,如果是则月数加一。 代码实现如下: import java.time.LocalDate; import java.time.Period; public class Date…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部