java中的浅拷贝和深拷贝测试

目录

1.浅拷贝

2.深拷贝-Cloneable

3.深拷贝-序列化


1.浅拷贝

浅拷贝时会创建一个新对象,这个对象有着原始对象属性值的一份精准拷贝。

属性是基本类型,拷贝的就是基本类型的值;

属性是引用类型,拷贝的就是内存地址;因此其中一个对象改变了这个地址,就会影响到另一个对象。

        测试:

a.创建一个User实体类,包含两个属性:name,clothes。并实现了Cloneable接口,重写了clone方法。

package clonePackage;

import java.util.List;

public class User implements Cloneable{
    private String name;//姓名
    private int age;//年龄
    private List<String> clothes;//衣服
    public User() {}

    public User(String name, int age, List<String> clothes) {
        this.name = name;
        this.age = age;
        this.clothes = clothes;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getClothes() {
        return clothes;
    }

    public void setClothes(List<String> clothes) {
        this.clothes = clothes;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", clothes=" + clothes +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

b.创建一个测试类进行测试

package clonePackage;

import java.util.ArrayList;
import java.util.List;

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        List<String> clothes = new ArrayList<>();
        clothes.add("夹克");
        clothes.add("牛仔裤");
        User user = new User("张三",18, clothes);
        User userClone = (User) user.clone();

        System.out.println(user);
        System.out.println(userClone);

        user.setName("李四");
        user.setAge(24);
        clothes.add("羽绒服");
        clothes.add("棉裤");

        System.out.println(user);
        System.out.println(userClone);
    }
}

运行结果如下:

        

         分析:

        1.userClone 克隆了user的基本类型的值(age),而对于引用类型的clothes其实只是克隆了其地址。当改变了user的age和clothes时,发现userClone的age没有发生变化,但是clothes却变了。

        2.在虚拟机中,他们的存储如下图:

 疑问:

        String 类型也是引用类型,为什么user的name属性被修改后,而userClone的name没有发生变化呢?

 解答:

        String类型比较特殊,它没有实现Cloneable接口,因此无法克隆,只能传递引用。在clone()后,指向的值为常量。克隆出来的对象改变他的值,实际上是改变了克隆出来对象String类型成员的指向,不会影响被克隆对象的值及其指向。

        因此String在拷贝的时候就表现出了“深拷贝”的特点;实际上String作为不可更改的类,在new赋值的时候,就已经创建了一个新的对象;

2.深拷贝-Cloneable

修改上述的User类中的clone方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        User user = (User) super.clone();
        List<String> list = new ArrayList<>(this.clothes);
        user.clothes = list;
        return user;
    }

        此时在clone的时候new了一个新的list,这样的话userClone就会指向新的地址。标号1的指向将不存在,而是在堆中开辟了新的空间(标号2)。

 此种改法会存在问题:

若是出现多个复杂类型的嵌套,或者User类中有其他类的引用,其他类中又存在复杂类型和别的类......,岂不是每个类都要实现Cloneable接口,并重写clone方法,超级麻烦。

3.深拷贝-序列化

1.User类实现Serializable接口

package clonePackage;

import java.io.Serializable;
import java.util.List;

public class User implements Serializable {
    private String name;//姓名
    private int age;//年龄
    private List<String> clothes;//衣服
    public User() {}

    public User(String name, int age, List<String> clothes) {
        this.name = name;
        this.age = age;
        this.clothes = clothes;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getClothes() {
        return clothes;
    }

    public void setClothes(List<String> clothes) {
        this.clothes = clothes;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", clothes=" + clothes +
                '}';
    }
}

2.创建一个深度拷贝的工具类

package clonePackage;

import java.io.*;

public class DeepCopyUtil {

    public static <T extends Serializable> T deepClone(T obj) {
        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();

            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }

}

测试如下:

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是用于复制对象的两种不同方式。 浅拷贝是创建一个新对象,并将原始对象的非静态字段的值复制到新对象。新对象和原始对象的引用类型字段将引用相同的对象。换句话说,浅拷贝只复制对象的引用,而不是对象本身。 深拷贝是创建一个新对象,并将原始对象的所有字段的值复制到新对象,包括引用类型字段。这意味着在深拷贝,即使原始对象的引用类型字段引用相同的对象,新对象也将有自己的副本。 为了实现浅拷贝,可以使用`clone()`方法。这个方法是`Object`类的一个方法,需要在要复制的类实现`Cloneable`接口。然后,可以使用`clone()`方法来创建一个新对象,它将具有与原始对象相同的字段值。 要实现深拷贝,可以通过以下几种方式之一: 1. 使用序列化和反序列化:将对象写入字节流并读取回来,这将创建一个与原始对象相同但独立的新对象。 2. 使用拷贝构造函数或拷贝工厂方法:在类定义一个构造函数或静态工厂方法,它接受另一个对象作为参数,并复制其字段值到新对象。 3. 递归复制对象的所有引用类型字段:对于每个引用类型字段,创建一个新对象并复制其字段值。 需要注意的是,深拷贝可能会导致性能开销较大,尤其是在对象图很大或存在循环引用的情况下。因此,在进行深拷贝时,需要仔细考虑其对性能的影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

科特er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值