【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,即在同一包内的类及不同包中的子类可见)。
相关推荐
Kay_Liang17 分钟前
大语言模型如何精准调用函数—— Function Calling 系统笔记
java·大数据·spring boot·笔记·ai·langchain·tools
自由的疯34 分钟前
Java 如何学习Docker
java·后端·架构
自由的疯35 分钟前
Java Docker本地部署
java·后端·架构
007php00737 分钟前
猿辅导Java面试真实经历与深度总结(二)
java·开发语言·python·计算机网络·面试·职场和发展·golang
摇滚侠40 分钟前
Spring Boot 3零基础教程,WEB 开发 内容协商机制 笔记34
java·spring boot·笔记·缓存
一勺菠萝丶40 分钟前
在 macOS 上用 Docker 为 Java 后端 & 常见开发需求搭建完整服务(详尽教程)
java·macos·docker
顾漂亮1 小时前
JVM底层攻坚
java·jvm·spring
编程岁月1 小时前
java面试-0215-HashMap有序吗?Comparable和Comparator区别?集合如何排序?
java·数据结构·面试
木井巳1 小时前
[Java数据结构与算法]详解排序算法
java·数据结构·算法·排序算法
没有bug.的程序员2 小时前
分布式架构未来趋势:从云原生到智能边缘的演进之路
java·分布式·微服务·云原生·架构·分布式系统