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

目录
  • Cloneable接口源码
  • 浅拷贝案例
    • Pet类定义
    • Person类定义
    • 浅拷贝问题-代码测试
  • 深拷贝案例
    • Pet类重写clone()方法
    • Person的clone()方法中调用Pet的clone方法
    • 浅拷贝问题解决-深拷贝代码测试
  • 总结

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方法

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

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

总结

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

(0)

相关推荐

  • JavaSE的三大接口:Comparator,Comparable和Cloneable详解

    进阶JavaSE-三大接口:Comparator.Comparable和Cloneable. Comparable和Comparator这两个接口很相似,都是用于比较大小的接口.在我们写一些数据结构的算法题时,用的比较多,具体是怎么用的,我们接着往下看. Comparator接口: public interface Comparator<T> { public int compare(T o1, T o2); //比较方法 } Comparable接口: public interface Co

  • Java的Comparable,Comparator和Cloneable三大接口详解

    目录 1.比较器 1.1Comparable接口 1.2Comparator接口 2.Cloneable接口 2.1深拷贝和浅拷贝 总结 1.比较器 ①比较器的引入 a.首先,当我们单一地比较某一种数据类型的数组时,可以直接用Arrays.sort()进行实现 b.而当我们同时含有多个参数时,并没有告诉我们按照什么来进行排序,此时,若是用Arrays.sort()就会出现报错的情况  基于这种情况,我们了解到,若是要将自定义类型进行大小比较 ,就要引入能够实现比较的接口,下面我们介绍Compar

  • Java中浅拷贝和深拷贝详解

    目录 Java浅拷贝深拷贝 实现浅拷贝 实现深拷贝 Java浅拷贝深拷贝 浅拷贝和深拷贝涉及到了Object类中的clone()方法 实现浅拷贝 浅拷贝的实现需要类重写clone()方法 浅拷贝会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝 如果属性是基本类型,拷贝的就是基本类型的值: 如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象,导致两个对象的引用不等. 实现浅拷贝很简单只需要将类实现Cloneable接口然后重写c

  • Java Clone深拷贝与浅拷贝的两种实现方法

    1.首先,你要知道怎么实现克隆:实现Cloneable接口,在bean里面重写clone()方法,权限为public. 2.其次,你要大概知道什么是地址传递,什么是值传递. 3.最后,你要知道你为什么使用这个clone方法. 先看第一条,简单的克隆代码的实现.这个也就是我们在没了解清楚这个Java的clone的时候,会出现的问题. 看完代码,我再说明这个时候的问题. 先看我要克隆的学生bean的代码: package com.lxk.model; /** * 学生类:有2个属性:1,基本属性-S

  • Java的深拷贝与浅拷贝的几种实现方式

    1.介绍 关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象.可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用传递将会有更深的理解. 2.浅拷贝 浅拷贝就是获得拷贝对象的引用,而不是正真意义上的拷贝一个对象,例如 A a = new A(); A b = a; 此时引用变量a和b 同时指向了同一个堆中的内存空间,变量b只是复制了实例A的引用地址,并不是重新在堆中开辟了一个新的空间位置,来完整的复制

  • Java的深拷贝和浅拷贝深入了解

    关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象.可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用传递将会有更深的理解. 1.创建对象的5种方式 ①.通过 new 关键字 这是最常用的一种方式,通过 new 关键字调用类的有参或无参构造方法来创建对象.比如 Object obj = new Object(); ②.通过 Class 类的 newInstance() 方法 这种默认是调用类的无参构

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

    目录 Cloneable接口源码 浅拷贝案例 Pet类定义 Person类定义 浅拷贝问题-代码测试 深拷贝案例 Pet类重写clone()方法 Person的clone()方法中调用Pet的clone方法 浅拷贝问题解决-深拷贝代码测试 总结 Cloneable接口源码 Cloneable接口: 实现此接口的类——可被推断java.lang.Object的clone()方法可以被合法调用-以实现类实例:属性到属性的拷贝. 如果一个类未实现Cloneable接口,那么调用clone()方法时,会

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

    目录 Cloneable接口源码 浅拷贝案例 Pet类定义 Person类定义 浅拷贝问题-代码测试 深拷贝案例 Pet类重写clone()方法 Person的clone()方法中调用Pet的clone方法 浅拷贝问题解决-深拷贝代码测试 总结 Cloneable接口源码 Cloneable接口: 实现此接口的类——可被推断java.lang.Object的clone()方法可以被合法调用-以实现类实例:属性到属性的拷贝. 如果一个类未实现Cloneable接口,那么调用clone()方法时,会

  • Java中接口和抽象类的区别详解

    需求:接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承实体类(concrete class)?抽象类中是否可以有静态的main方法? 先说明二者的定义,然后聊聊需求,最后分析二者的区别. 含有abstract修饰符的类即为抽象类,抽象类不能创建实例对象.含有抽象方法的类必须定义为abstract class.在abstract class中,方法不必是抽象的,但是抽象方法必须在具体子类中实现,所以,不能有抽象构造方法或抽象静态方法.子类如果没有实现抽象父类中的所

  • JavaScript中深拷贝与浅拷贝详解

    目录 1 浅拷贝概念 2 深拷贝概念 3 浅拷贝的实现方式 3.1 Object.assign() 3.2 Array.prototype.concat() 3.3 Array.prototype.slice() 3.4 直接赋值 4 深拷贝的实现方式 4.1 JSON.parse(JSON.stringify()) 4.2 函数库lodash 总结 1 浅拷贝概念 深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的. 浅拷贝是创建一个新对象,该对象有着原始对象属性值的一份精确拷

  • C语言结构体成员赋值的深拷贝与浅拷贝详解

    目录 浅拷贝 结构体中不存在指针成员变量时 结构体中存在指针成员变量时 深拷贝 结论 浅拷贝 C语言中的浅拷贝是指在拷贝过程中,对于指针型成员变量只拷贝指针本身,而不拷贝指针所指向的目标,它按字节复制的.我们分几种情况举例子来看一下. 结构体中不存在指针成员变量时 代码如下: #include <stdio.h> typedef struct { char name[64]; int age; }Member; int main(){ Member stu1 = { "LiMing&

  • Python对象的深拷贝和浅拷贝详解

    本文内容是在<Python核心编程2>上看到的,感觉很有用便写出来,给大家参考参考! 浅拷贝 首先我们使用两种方式来拷贝对象,一种是切片,另外一种是工厂方法.然后使用id函数来看看它们的标示符 复制代码 代码如下: # encoding=UTF-8   obj = ['name',['age',18]] a=obj[:] b=list(obj) for x in obj,a,b:     print id(x)   35217032 35227912 29943304 他们的id都不同,按照正

  • C++拷贝构造函数(深拷贝与浅拷贝)详解

    对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. 复制代码 代码如下: #include <iostream>using namespace std; class CExample {private:    int a;public:    CExample(int b)    { a=b;}    void Show ()    {       

  • Python中的深拷贝和浅拷贝详解

    要说清楚Python中的深浅拷贝,需要搞清楚下面一系列概念: 变量-引用-对象(可变对象,不可变对象)-切片-拷贝(浅拷贝,深拷贝) [变量-对象-引用] 在Python中一切都是对象,比如说:3, 3.14, 'Hello', [1,2,3,4],{'a':1}...... 甚至连type其本身都是对象,type对象 Python中变量与C/C++/Java中不同,它是指对象的引用,Python是动态类型,程序运行时候,会根据对象的类型 来确认变量到底是什么类型. 单独赋值: 比如说: 复制代

  • javascript深拷贝和浅拷贝详解

    一.数组的深浅拷贝 在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生. 这是为什么呢? 因为如果只是简单的赋值,它只是进行了地址的引用,所以改变一个另一个也会跟着变. var arr = ["One","Two","Three"]; var arrto = arr; arrto[1] = "te

  • java单机接口限流处理方案详解

    对单机服务做接口限流的处理方案 简单说就是设定某个接口一定时间只接受固定次数的请求,比如/add接口1秒最多接收100次请求,多的直接拒绝,这个问题很常见,场景也好理解,直接上代码: /** * 单机限流 */ @Slf4j public class FlowLimit { //接口限流上限值和限流时间缓存 private static Cache<String, AtomicLong> localCache = CacheBuilder.newBuilder().maximumSize(10

随机推荐