【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,即在同一包内的类及不同包中的子类可见)。
相关推荐
future141227 分钟前
C#每日学习日记
java·学习·c#
一个混子程序员33 分钟前
SpringBoot自定义Schedule注解
java
心之语歌37 分钟前
Java高效压缩技巧:ZipOutputStream详解
java·后端
booooooty40 分钟前
基于Spring AI Alibaba的多智能体RAG应用
java·人工智能·spring·多智能体·rag·spring ai·ai alibaba
猴哥源码41 分钟前
基于Java+SpringBoot的健身房管理系统
java·spring boot
极光雨雨1 小时前
Spring Bean 控制销毁顺序的方法总结
java·spring
猴哥源码1 小时前
基于Java+SpringBoot的三国之家网站
java·spring boot
念九_ysl1 小时前
Java 使用 OpenHTMLToPDF + Batik 将含 SVG 遮罩的 HTML 转为 PDF 的完整实践
java·开发语言·pdf
yaoxin5211231 小时前
124. Java 泛型 - 有界类型参数
java·开发语言
Spirit_NKlaus1 小时前
解决HttpServletRequest无法获取@RequestBody修饰的参数
java·spring boot·spring