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方法。

相关推荐
J-Tony1113 小时前
【JVM】三色标记法
java·jvm·算法
辣椒思密达14 小时前
Python公开数据采集实战:如何解决请求高频拦截与Session会话中断问题
开发语言·python
李白的天不白14 小时前
docker ps
java
Albart57514 小时前
Python 实战教程:用 30 分钟学会解决真实问题
开发语言·python
NE_STOP14 小时前
Docker--Docker Swarm集群
java
2301_7736436214 小时前
ceph池
开发语言·ceph·python
两年半的个人练习生^_^14 小时前
JMM 进阶:彻底理解 CAS 实现原理
java·开发语言
wuminyu14 小时前
Java锁机制之park和unpark源码剖析
java·linux·c语言·jvm·c++
半个烧饼不加肉14 小时前
JS 底层探究-- 事件循环
开发语言·前端·javascript
W_LuYi18515 小时前
手撸极简zkEVM验证器:RISC-V电路实践
java·risc-v