Java拷贝之深拷贝与浅拷贝

Java中的拷贝分为引用拷贝和对象拷贝

1、引用拷贝(浅拷贝)

引用拷贝:只会生成一个新的对象引用地址,但两个对峙最终指向的还是同一个对象

代码示例:

java 复制代码
// 定义的测试对象
@Data
public class User {
    private String name;
    private String age;
    private String address;
    public User(String name, String age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
}

// 测试类
@Test
void testCopy() {
    User userA = new User("john", "23", "西斗门路");
    System.out.println(userA);
    User userB = userA;
    userB.setName("matt");
    System.out.println("userA:" + userA);
    System.out.println("userB:" + userB);
}

/** 执行结果
 * User(name=john, age=23, address=西斗门路)
 * userA:User(name=matt, age=23, address=西斗门路)
 * userB:User(name=matt, age=23, address=西斗门路)
 */

可以看到,在修改userB的属性后,userA的属性也一同被修改了,说明此时二者的引用对象都是同一个。

2、对象拷贝(深拷贝/浅拷贝)

对象拷贝:会重新生成一个对象,新生成的对象和之前的对象没有任何关联。

对象拷贝中,区分为两类:对象浅拷贝、对象深拷贝

2.1、对象浅拷贝

在克隆操作中国,只复制对象本身以及对象内部的基本数据类的属性,而不会复制对象内部的引用类型的属性。

浅拷贝仅仅创建了一个新的对象,该对象与原对象的引用类型属性还是指向同一地址,当应用类型发生修改时,二者都会变更。

在浅拷贝中,新对象和原始对象指向同一块内存区域,因此对其中一个对象进行修改可能会影响到另一个对象。

想要实现对象浅拷贝,可以通过实现Cloneable接口重写其clone方法

代码示例:

java 复制代码
// 定义的测试对象
@Data
public class User implements Cloneable{
    private String name;
    private String age;
    private String address;
    private Father father;
    public User(String name, String age, String address, Father father) {
        this.name = name;
        this.age = age;
        this.address = address;
        this.father = father;
    }
    public User copyUser() throws CloneNotSupportedException {
        return (User) clone();
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
@Data
public class Father {
    private String name;
    public Father(String name) {
        this.name = name;
    }
}

@Test
void testCopy() {
    Father fatherA = new Father("William");
    User userA = new User("john", "23", "西斗门路", fatherA);
    System.out.println(userA);
    User userB = null;
    try {
        userB = userA.copyUser();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    userB.setName("matt");
    userB.getFather().setName("Billion");
    System.out.println("userA:" + userA);
    System.out.println("userB:" + userB);
}

/**
 * 执行结果
 * User(name=john, age=23, address=西斗门路, father=Father(name=William))
 * userA:User(name=john, age=23, address=西斗门路, father=Father(name=Billion))
 * userB:User(name=matt, age=23, address=西斗门路, father=Father(name=Billion))
 */

2.2、对象深拷贝

  • 深拷贝指的是在复制对象的过程中,除了复制对象及其基本类型的属性时,还要递归的复制对象内部的引用类型的属性。
  • 深拷贝会创建一个完全独立的对象, 新对象和原对象不会产生任何关联,各自的属性值修改互不影响

实现有两个方案:

1、实现Serializable接口,通过序列化方式进行克隆(下面示例使用方式)

2、重写Cloneable的clone方法,即对原对象的引用类型的属性二次调用clone方法进行克隆

代码示例:

java 复制代码
// 创建对象,注意都需要实现Serializable接口
@Data
public class User implements Serializable {
    private String name;
    private String age;
    private String address;
    private Father father;
    public User(String name, String age, String address, Father father) {
        this.name = name;
        this.age = age;
        this.address = address;
        this.father = father;
    }
}

@Data
public class Father implements Serializable {
    private String name;
    public Father(String name) {
        this.name = name;
    }
}

// 通过序列化实现深拷贝的方法
public static <T extends Serializable> T deepCopy(T object) {
    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
        // 序列化对象
        objectOutputStream.writeObject(object);
        objectOutputStream.flush();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
             ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
            // 反序列化对象,实现深拷贝
            return (T) objectInputStream.readObject();
        }
    } catch (IOException | ClassNotFoundException e) {
        // 处理n异常
        e.printStackTrace();
    }
    // 返回null,表示深拷贝失败
    return null;
}

// 测试
@Test
void testCopy() {
    Father fatherA = new Father("William");
    User userA = new User("john", "23", "西斗门路", fatherA);
    System.out.println(userA);
    User userB = deepCopy(userA);
    userB.setName("matt");
    userB.getFather().setName("Billion");
    System.out.println("userA:" + userA);
    System.out.println("userB:" + userB);
}

/**
 * 执行结果
 * User(name=john, age=23, address=西斗门路, father=Father(name=William))
 * userA:User(name=john, age=23, address=西斗门路, father=Father(name=William))
 * userB:User(name=matt, age=23, address=西斗门路, father=Father(name=Billion))
 */

附带知识:使用try-with-resources:在Java 7及更高版本中,可以使用try-with-resources语句来自动关闭实现了Closeable接口的资源。在这段代码中,ByteArrayOutputStream、ObjectOutputStream、ByteArrayInputStream和ObjectInputStream都实现了Closeable接口,因此可以使用try-with-resources来自动关闭这些资源,而无需手动调用close方法。

相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔6 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海7 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai11 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说11 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json