【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,即在同一包内的类及不同包中的子类可见)。
相关推荐
User_芊芊君子2 小时前
【JavaSE】复习总结
java·开发语言·python
我有一颗五叶草2 小时前
线程间通信
java·开发语言
我真的是大笨蛋6 小时前
K8S-Pod(下)
java·笔记·云原生·容器·kubernetes
碳水加碳水7 小时前
Java代码审计实战:XML外部实体注入(XXE)深度解析
java·安全·web安全·代码审计
努力也学不会java8 小时前
【设计模式】 原型模式
java·设计模式·原型模式
方渐鸿8 小时前
【2024】k8s集群 图文详细 部署安装使用(两万字)
java·运维·容器·kubernetes·k8s·运维开发·持续部署
学亮编程手记8 小时前
K8S v1.33 版本主要新特性介绍
java·容器·kubernetes
Haven-9 小时前
Java-面试八股文-JVM篇
java·jvm·面试
我真的是大笨蛋9 小时前
JVM调优总结
java·jvm·数据库·redis·缓存·性能优化·系统架构
wjs04010 小时前
Git常用的命令
java·git·gitlab