Java Cloneable接口的深拷贝与浅拷贝详解

 更新时间:2022年03月17日 15:14:07   作者:似曾不相识  
这篇文章主要为大家详细介绍了Java Cloneable接口的深拷贝与浅拷贝,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

Cloneable接口源码

Cloneable接口:

实现此接口的类——可被推断java.lang.Object的clone()方法可以被合法调用-以实现类实例:属性到属性的拷贝。

如果一个类未实现Cloneable接口,那么调用clone()方法时,会抛出CloneNotSupportedException异常。

通常,实现了Cloneable接口的子类,应当以public访问权限重写clone()方法(尽管java.Object类中的clone方法是protected类型的)

应当认识到:Cloneable接口没有包含clone()方法,因此,如果仅仅是implements了Cloneable接口,那么也是无法正常克隆对象的

[原因:即使是反射性地调用了克隆方法,也不能保证它一定会成功]——个人理解就是:是否重写Clone()方法、或者“浅拷贝与深拷贝”问题的存在所导致的。

class Pet implements Cloneable{
    //properties
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public Pet() {
    }
    public Pet(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pet pet = (Pet) o;
        return Objects.equals(name, pet.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
//    @Override
//    public Pet clone() {
//        try {
//            return (Pet)super.clone();
//        } catch (CloneNotSupportedException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }
}

浅拷贝案例

Pet类定义

注意到:Pet类实现了Cloneable接口,但是没有重写Clone()方法(显然:此时Pet类不具备对象克隆的能力)。

class Pet implements Cloneable{
    //properties
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public Pet() {
    }
    public Pet(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pet pet = (Pet) o;
        return Objects.equals(name, pet.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
//    @Override
//    public Pet clone() {
//        try {
//            return (Pet)super.clone();
//        } catch (CloneNotSupportedException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }
}

Person类定义

注意到:Person类实现了Cloneable接口,也重写Clone()方法。那么,是否Person类就具备了对象克隆的能力呢?(由于浅拷贝问题的存在,认为这种对象克隆能力是不完整的、有缺陷的)。

class Pet implements Cloneable{
    //properties
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public Pet() {
    }
    public Pet(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pet pet = (Pet) o;
        return Objects.equals(name, pet.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
//    @Override
//    public Pet clone() {
//        try {
//            return (Pet)super.clone();
//        } catch (CloneNotSupportedException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }
}

浅拷贝问题-代码测试

为什么说:此时Person类的对象克隆能力是不完整的、有缺陷的?因为此时,在通过Person对象调用clone()方法,克隆对象时,其成员属性pet(Pet类的对象)值的克隆,仅仅是对堆区内存地址的简单拷贝。

即:说白了,Person对象和克隆出来的对象,其pet属性值共享同一块堆区内存。——问题显而易见:当对克隆出来的对象的pet属性进行set操作时,显然会影响到原始Person对象的pet属性值。

代码演示如下:

  //methods
    public static void main(String[] args) throws CloneNotSupportedException {
        testPerson();
    }
    public static void testPerson() throws CloneNotSupportedException {
        Person p=new Person("张三",14,new Pet("小黑"));
        System.out.println(p);
        Person clone = (Person)p.clone();
        System.out.println(clone);
        System.out.println(p.equals(clone));
        System.out.println(p.getPet()==clone.getPet());
        System.out.println("************");
        clone.setAge(15);
        System.out.println(p);
        System.out.println(clone);
        System.out.println(p.equals(clone));
        System.out.println("************");
        clone.getPet().setName("小黄");
        System.out.println(p);
        System.out.println(clone);
        System.out.println(p.equals(clone));
        System.out.println(p.getPet()==clone.getPet());
    }

在这里插入图片描述

深拷贝案例

那么,如何实现深拷贝呢?关键就在上述案例中,被注释的几行代码中。

Pet类重写clone()方法

在这里插入图片描述

Person的clone()方法中调用Pet的clone方法

在这里插入图片描述

浅拷贝问题解决-深拷贝代码测试

测试代码不变,再次运行:

在这里插入图片描述

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!      

相关文章

  • Windows10系统下JDK1.8的下载安装及环境变量配置的教程

    Windows10系统下JDK1.8的下载安装及环境变量配置的教程

    这篇文章主要介绍了Windows10系统下JDK1.8的下载安装及环境变量配置的教程,本文图文并茂给大家介绍的非常详细,对大家的工作或学习具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • 用Java实现一个简单的布隆过滤器

    用Java实现一个简单的布隆过滤器

    这篇文章主要介绍了用Java实现一个简单的布隆过滤器,布隆过滤器是1970年由布隆提出的,它实际上是一个很长的二进制向量和一系列随机映射函数,布隆过滤器可以用于检索一个元素是否在一个集合中,需要的朋友可以参考下
    2023-12-12
  • java使用BeanUtils.copyProperties方法对象复制同名字段类型不同赋值为空问题解决方案

    java使用BeanUtils.copyProperties方法对象复制同名字段类型不同赋值为空问题解决方案

    这篇文章主要给大家介绍了关于java使用BeanUtils.copyProperties方法对象复制同名字段类型不同赋值为空问题的解决方案,文中通过代码介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-11-11
  • java实现饭店点菜系统

    java实现饭店点菜系统

    这篇文章主要为大家详细介绍了java实现饭店点菜系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 如何在JAVA中使用Synchronized

    如何在JAVA中使用Synchronized

    这篇文章主要介绍了如何在JAVA中使用Synchronized,文中代码非常详细,对大家的学习有所帮助,感兴趣的朋友可以参考下
    2020-06-06
  • SpringBoot中使用RocketMQ的示例代码

    SpringBoot中使用RocketMQ的示例代码

    本文主要介绍SpringBoot中使用RocketMQ的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • springboot接收日期类型参数的操作方法

    springboot接收日期类型参数的操作方法

    如果使用Get请求,直接使用对象接收,则可以使用@DateTimeFormat注解进行格式化,本文重点给大家介绍springboot接收日期类型参数的方法,感兴趣的朋友一起看看吧
    2024-02-02
  • Java多态性定义与用法实例详解

    Java多态性定义与用法实例详解

    这篇文章主要介绍了Java多态性定义与用法,较为详细的分析了多态的概念、功能以及java定义与实现面向对象多态性的相关操作技巧,需要的朋友可以参考下
    2017-09-09
  • sentinel整合ribbon与fallback流程分步讲解

    sentinel整合ribbon与fallback流程分步讲解

    这篇文章主要介绍了sentinel整合ribbon与fallback分步流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • 微信公众帐号开发-自定义菜单的创建及菜单事件响应的实例

    微信公众帐号开发-自定义菜单的创建及菜单事件响应的实例

    本篇文章主要介绍了微信公众帐号开发-自定义菜单的创建及菜单事件响应的实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-12-12

最新评论