【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,即在同一包内的类及不同包中的子类可见)。
相关推荐
我命由我123452 小时前
Kotlin 数据容器 - List(List 概述、创建 List、List 核心特性、List 元素访问、List 遍历)
java·开发语言·jvm·windows·java-ee·kotlin·list
武子康5 小时前
Java-80 深入浅出 RPC Dubbo 动态服务降级:从雪崩防护到配置中心秒级生效
java·分布式·后端·spring·微服务·rpc·dubbo
YuTaoShao7 小时前
【LeetCode 热题 100】131. 分割回文串——回溯
java·算法·leetcode·深度优先
源码_V_saaskw7 小时前
JAVA图文短视频交友+自营商城系统源码支持小程序+Android+IOS+H5
java·微信小程序·小程序·uni-app·音视频·交友
超浪的晨8 小时前
Java UDP 通信详解:从基础到实战,彻底掌握无连接网络编程
java·开发语言·后端·学习·个人开发
双力臂4048 小时前
Spring Boot 单元测试进阶:JUnit5 + Mock测试与切片测试实战及覆盖率报告生成
java·spring boot·后端·单元测试
Edingbrugh.南空9 小时前
Aerospike与Redis深度对比:从架构到性能的全方位解析
java·开发语言·spring
QQ_4376643149 小时前
C++11 右值引用 Lambda 表达式
java·开发语言·c++
永卿0019 小时前
设计模式-迭代器模式
java·设计模式·迭代器模式
誰能久伴不乏9 小时前
Linux如何执行系统调用及高效执行系统调用:深入浅出的解析
java·服务器·前端