【Java】谈一谈浅克隆和深克隆

关于深、浅clone的描述

浅克隆:对当前对象进行克隆,并克隆该对象所包括的8种基本数据类型String类型属性(copy一份该对象并重新分配内存,产生新的对象),但如果被克隆的对象还包括除了8种基本类型和String类型之外的其他类型属性,则浅克隆不会克隆这些属性(不会为这些属性分配内存,而是引用原来的属性)。

深克隆:在浅克隆的基础上,递归的克隆会为所有类型属性重新分配内存,而不是像浅clone一样:8种基本类型 + String类型以外的属性引用原对象的属性。

实现clone的要求

要使对象实现克隆,需要让对象实体类实现Cloneable接口,否则实现类中调用重写的clone()方法会报CloneNotSupportedException异常。抛出该异常表示:调用了Object类的clone()方法克隆了一个对象,但是该对象的类没有实现Cloneable接口。(覆盖clone()方法的应用程序也可以抛出此异常,表示无法或不应clone对象)

Cloneable接口是一个标记接口,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功。

代码案例

说明:对user对象设计深浅clone逻辑,并且运行测试用例验证clone原理。

User类的基本设计:

java 复制代码
@Getter
@Setter
public class User implements Serializable {
    private static final long serialVersionUID = -1241246044434342674L;
    private String username;
    private String gender;
    private Date createTime;

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        User user = (User) o;
        return Objects.equals(username, user.username) && Objects.equals(gender, user.gender) && Objects.equals(createTime, user.createTime);
    }

    @Override
    public int hashCode() {
        return Objects.hash(username, gender, createTime);
    }
}

重写Object的clone()方法但不实现Cloneable接口

在User类中重写clone()方法:

java 复制代码
@Override
public Object clone() throws CloneNotSupportedException {
    // 此处直接继承的Object的clone()方法,为浅clone
    return super.clone();
}

测试用例:

java 复制代码
@Test
public void testShallowClone() {
    try {
        User user = new User();
        user.setUsername("Black");
        user.setGender("Male");
        user.setCreateTime(new Date());
        User cloneUser = (User) user.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
}

执行结果: (抛出CloneNotSupportedException异常)

浅clone(已实现Cloneable接口)

在User类中实现Cloneable接口:

java 复制代码
@Getter
@Setter
public class User implements Cloneable, Serializable {

    // User类的基本设计,省略
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

测试用例:

java 复制代码
try {
    Date currentTime = new Date();
    User user = new User();
    user.setUsername("Black");
    user.setGender("Male");
    user.setCreateTime(currentTime);
    User cloneUser = (User) user.clone();
    System.out.println("user: " + JSON.toJSONString(user) + ", type: " + user.getClass().getName());
    System.out.println("cloneUser: " + JSON.toJSONString(cloneUser) + ", type: " + user.getClass().getName());
    System.out.println(user.equals(cloneUser));
    currentTime.setTime(currentTime.getTime() + 1000);
    System.out.println("----------");
    System.out.println("user: " + JSON.toJSONString(user) + ", type: " + user.getClass().getName());
    System.out.println("cloneUser: " + JSON.toJSONString(cloneUser) + ", type: " + user.getClass().getName());
    System.out.println(user.equals(cloneUser));
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

执行结果: (更改Date类型的createTime值之后,两个对象仍相同,说明引用的是原先的属性。)

深clone

重写User类中的clone()方法:

java 复制代码
@Override
public Object clone() throws CloneNotSupportedException {
    Object obj = super.clone();
    User user = (User) obj;
    user.createTime = (Date) this.createTime.clone();
    return obj;
}

测试用例:(同浅clone)

执行结果:

(更改Date类型的createTime值之后,两个对象不相同了,说明给clone对象重新分配了内存。)

小结

  • 要实现clone,除了需要重写Object的clone()方法外,还需要实现Cloneable接口,以向 java.lang.Objectclone() 方法表示该方法创建该类实例的逐个字段副本是合法的;
  • 在未实现 Cloneable 接口的实例上调用 Object 的 clone() 方法会导致抛出CloneNotSupportedException异常;
  • 实现此接口的类应使用公共方法(修改访问权限为public)覆盖 Object.clone()(clone()方法的默认访问权限为protected,即在同一包内的类及不同包中的子类可见)。
相关推荐
NE_STOP4 分钟前
shiro_实现分布式会话SessionManager、限制密码重试次数和并发登录控制
java
Seven977 分钟前
剑指offer-63、数据流中的中位数
java
毕设源码-钟学长9 分钟前
【开题答辩全过程】以 基于Spring Boot的社区养老服务管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
mjhcsp33 分钟前
C++ Manacher 算法:原理、实现与应用全解析
java·c++·算法·manacher 算法
Coder_Boy_34 分钟前
基于SpringAI的在线考试系统-企业级软件研发工程应用规范案例
java·运维·spring boot·软件工程·devops
indexsunny36 分钟前
互联网大厂Java面试实战:微服务、Spring Boot与Kafka在电商场景中的应用
java·spring boot·微服务·面试·kafka·电商
SUDO-144 分钟前
Spring Boot + Vue 2 的企业级 SaaS 多租户招聘管理系统
java·spring boot·求职招聘·sass
sheji34161 小时前
【开题答辩全过程】以 基于spring boot的停车管理系统为例,包含答辩的问题和答案
java·spring boot·后端
重生之后端学习1 小时前
21. 合并两个有序链表
java·算法·leetcode·链表·职场和发展
南屿欣风1 小时前
Sentinel 熔断规则 - 异常比例(order & product 示例)笔记
java·开发语言