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日

相关文章

  • Java8优雅的字符串拼接工具类StringJoiner实例代码

    下面是关于“Java8优雅的字符串拼接工具类StringJoiner实例代码”的完整攻略。 什么是StringJoiner StringJoiner是Java 8中提供的一个字符串拼接工具类。它可以将多个字符串按照指定的分隔符连接起来,并可以指定前缀和后缀,从而生成一个完整的字符串。 StringJoiner的构造方法 public StringJoiner…

    Java 2023年5月26日
    00
  • java工具类StringUtils使用实例详解

    Java工具类StringUtils使用实例详解 什么是StringUtils StringUtils 是一个Apache Commons Lang库中的工具类,提供一系列处理字符串的静态方法。该类提供了一些我们常用的字符串操作方法,比如字符串为空、字符串不为空或者为null、去除字符串两端的空格等。 StringUtils的导入方式 如需使用StringU…

    Java 2023年5月27日
    00
  • Java实现文件上传的方法

    下面是Java实现文件上传的方法的完整攻略。 概述 在一些Web应用中,我们需要实现文件上传功能。Java 语言提供了多种方法,使得文件上传变得简单、易于管理。本文将简述Java实现文件上传的方法,包括基础知识、实现示例、注意事项等。 基础知识 在 Java 中,实现文件上传通常需要完成以下几个步骤: 在前端页面中添加一个文件上传的表单元素,以便用户上传需要…

    Java 2023年5月19日
    00
  • Spring 代码技巧梳理总结让你爱不释手

    Spring 代码技巧梳理总结让你爱不释手攻略 介绍 Spring是一款开源的,轻量级的Java开发框架。它包含了一系列的工具,使得Java开发更加容易和高效。在本攻略中,我们会总结出一些Spring开发中的常用技巧,以帮助你更加熟悉和熟练地使用Spring。 技巧列表 使用@Autowired简化依赖注入 在Spring中,我们可以使用@Autowired…

    Java 2023年5月19日
    00
  • 解决出现 java.lang.ExceptionInInitializerError错误问题

    解决Java程序中出现java.lang.ExceptionInInitializerError错误问题,通常需要以下几个步骤。 步骤一:定位错误原因 在 Java 程序中出现 java.lang.ExceptionInInitializerError 错误,通常是由于静态初始化块抛出了异常引起的。因此,要定位错误原因,需要查看这个静态初始化块的代码,找出导…

    Java 2023年5月27日
    00
  • Java date format时间格式化操作示例

    当我们在Java中操作日期和时间相关业务时,经常需要进行时间格式化的操作,这时候就需要用到Java的Date和SimpleDateFormat类来进行转化和格式化。下面就是Java date format时间格式化操作示例的完整攻略。 步骤1:导入相关类库 在进行Java时间格式化操作前,需要先导入相关的类库。 import java.util.Date; …

    Java 2023年5月26日
    00
  • SpringBoot常用注解详细整理

    SpringBoot常用注解详细整理 什么是SpringBoot注解 Spring Boot提供了许多注解来简化Spring应用程序的开发和配置。在Spring中,注解使得我们能够重用代码、简化配置和提供了一致性数据。Spring Boot重度依赖注解,是设计成可以快速使用注解来进行Spring应用程序的开发和配置,从而节省了开发人员的时间和精力。 常用注解…

    Java 2023年5月15日
    00
  • 如何把本地jar包导入maven并pom添加依赖

    下面是如何把本地jar包导入maven并pom添加依赖的完整攻略: 1. 将本地jar包导入maven仓库 使用本地jar包,我们需要先将其导入maven仓库里面,这样我们才能在pom文件中引用到它。 步骤如下: 打开命令行窗口,进入到本地jar包所在目录 假设本地jar包文件名为example.jar,执行以下命令: shell mvn install:i…

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