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

yizhihongxing

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日

相关文章

  • 使用maven插件对java工程进行打包过程解析

    使用 Maven 插件可以非常方便地对 Java 工程进行打包。下面是使用 maven-assembly-plugin 对 Java 工程进行打包的攻略: 1. 添加 Maven 插件 在 pom.xml 文件中添加 maven-assembly-plugin 插件的依赖: <build> <plugins> <plugin&g…

    Java 2023年5月20日
    00
  • SpringMvc获取请求头请求体消息过程解析

    Spring MVC获取请求头请求体消息过程解析 什么是请求头和请求体 在HTTP协议中,请求报文分为请求头和请求体两部分。其中请求头包含了一些元数据,如请求方式、请求地址、请求头部信息等;而请求体则是一些用作请求参数的数据,如表单提交、json数据等。 Spring MVC获取请求头信息 Spring MVC框架中,我们可以通过@RequestHeader…

    Java 2023年6月15日
    00
  • Java日常练习题,每天进步一点点(58)

    以下是对Java日常练习题攻略的详细讲解。 1. 了解练习题目的类型和难度 在开始练习之前,首先要了解练习题目的类型和难度。这些练习题目主要包括数据类型、数组、字符串处理等基础知识,以及流程控制语句、循环语句等基础流程控制语句,具有一定的难度。因此,在练习之前,建议先了解这些基础知识和语句,再根据自己的水平选择不同难度的练习题。 2. 流程掌握 在做练习题之…

    Java 2023年5月20日
    00
  • java银行管理系统源码

    Java银行管理系统源码攻略 介绍 本文将介绍Java银行管理系统源码的详细攻略,包括安装、配置、使用等过程。Java银行管理系统是一款非常实用的软件,可以帮助用户管理银行账户、转账、存款、取款等操作。使用该系统可以大大提升工作效率和管理银行的准确性。本文将详细介绍该系统的安装和使用过程。 安装 Java银行管理系统源码需要在Java开发环境下进行安装和配置…

    Java 2023年5月23日
    00
  • Java如何把数组转换为ArrayList

    将Java中的数组转换为ArrayList的过程可以分为两个步骤: 创建一个ArrayList对象 将数组中的元素添加到ArrayList中 以下是完整的转换过程及示例说明: 创建ArrayList对象 要将数组转换为ArrayList,首先需要创建一个新的ArrayList对象。可以使用泛型来定义ArrayList存储的数据类型,根据需要创建一个整型Arr…

    Java 2023年5月26日
    00
  • Java终止线程实例和stop()方法源码阅读

    Java中有多种方式可以终止线程,其中包括共享变量、使用interrupt()方法、使用stop()方法等,本文着重讲解stop()方法的使用和源码阅读。 1. stop()方法的使用 stop()方法是Thread类中提供的方法,可以立即终止线程的执行。但是,它并不推荐使用,因为它可能会导致线程未能完成它该做的任务,引发不确定的错误。 下面是stop()方…

    Java 2023年5月19日
    00
  • java获得平台相关的行分隔符和java路径分隔符的方法

    获取平台相关的行分隔符方法: 在Java程序中,我们需要将字符串或数据写入到文件或网络中,而不同的操作系统使用不同的转义符进行换行操作。因此,我们需要获得与操作系统相关的行分隔符,以便在正确的位置进行换行操作。 Java中可以通过System.getProperty()方法获取平台相关的行分隔符。该方法返回操作系统的行分隔符,可以在不同的平台上使用相同的代码…

    Java 2023年5月26日
    00
  • Java Structs框架原理案例详解

    Java Structs框架原理案例详解 什么是Struts框架 Struts框架是一种流行的Java Web应用程序框架,Struts采用MVC(Model-View-Controller)架构模式,将应用程序的数据(Model),用户界面(View)和控制逻辑(Controller)清晰地分离开,以便维护和扩展应用程序。 Struts框架包含许多重要的组…

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