Java的浅拷贝与深拷贝

1.基本类型与引用类型

  • 基本类型:基本类型也称为值类型,分别是字符类型 char,布尔类型 boolean以及数值类型
    byte、short、int、long、float、double。
  • 引用类型:包括类、接口、数组、枚举等。

Java 将内存空间分为堆和栈。基本类型直接在栈中存储数值,而引用类型是将引用放在栈中,实际存储的值是放在堆中,通过栈中的引用指向堆中存放的数据。

2.浅拷贝

浅拷贝实现很简单,只要让需要拷贝的类实现Cloneable接口并重写clone方法即可。浅拷贝中只是将基本类型复制到栈空间中,引用类型的数据仅仅在栈中创建了对象名,对象名还是指向堆中的原数据,所以在改变引用对象时,浅复制过去的引用类型都会改变,因为引用类型在堆中为同一个。

专业类:

public class Major {
    public Major(String name, int id) {
        this.name = name;
        this.id = id;
    }

    private String name;
    private int id;

...省略get、set、toString方法...
}

学生类:

public class Student implements Cloneable{

    public Student(int id, String name, Major m) {
        this.id = id;
        this.name = name;
        this.m = m;
    }

    private int id;
    private String name;
    private Major m;


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

...省略get、set、toString方法...

    public static void main(String[] args) throws CloneNotSupportedException{
        Major major = new Major("信息",11);
        Student student1 = new Student(22,"李四",major);
        Student student2 = (Student)student1.clone();
        System.out.println("修改前1为"+student1.toString());
        System.out.println("修改前2为"+student2.toString());
        major.setId(33);
        major.setName("城建");
        student1.setId(44);
        student1.setName("张三");
        System.out.println("修改后1为"+student1.toString());
        System.out.println("修改后2为"+student2.toString());
    }
}

结果为:在修改了Student1中的Major对象后,发现Student2中的Major也会改变。

修改前1为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
修改前2为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
修改后1为Student{id=44, name=’张三’, m=Major{name=’城建’, id=33}}
修改后2为Student{id=22, name=’李四’, m=Major{name=’城建’, id=33}}

3.深拷贝

深拷贝完完全全的对一个对象进行了复制,无论是栈还是堆中内容都会改变。在改变原对象后拷贝后的对象不会发生改变。实现深拷贝的方式如下:

  • 让每个引用类型属性内部都重写clone()方法。如Student中有引用类型Major,所以Major需要实现Cloneable接口并重写clone()方法。

专业类修改为:

public class Major implements Cloneable{
    public Major(String name, int id) {
        this.name = name;
        this.id = id;
    }

    private String name;
    private int id;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
...省略方法...
}

学生类重写的clone方法变成:

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.m=(Major) m.clone();
        return student;
    }

结果为:

修改前1为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
修改前2为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}
修改后1为Student{id=44, name=’张三’, m=Major{name=’城建’, id=33}}
修改后2为Student{id=22, name=’李四’, m=Major{name=’信息’, id=11}}

  • 序列化实现深拷贝:序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。如果嵌套多层引用类型,建议使用反序列化方式实现深拷贝。

每个需要序列化的类都要实现 Serializable 接口,如果有某个属性不需要序列化,可以将其声明为 transient,即将其排除在克隆属性之外。

Student需要完成深拷贝,由于Student中存在引用类型Major,所以在Student、Major中实现Serializable接口,并在Student类中添加深拷贝方法(这种方法不需要在实现Cloneable接口并重写clone()方法了)。主要的方法体为:

//序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);

//反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();

专业类:

public class Major implements Serializable{
    public Major(String name, int id) {
        this.name = name;
        this.id = id;
    }

    private String name;
    private int id;

...省略get、set、toString方法...
}

学生类:

public class Student implements Serializable {

    public Student(int id, String name, Major m) {
        this.id = id;
        this.name = name;
        this.m = m;
    }

    private int id;
    private String name;
    private Major m;

   //深拷贝方法
   public Object deepClone() throws Exception{
        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        //反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }

...省略方法...
}

本人小白一个,如果有什么问题还请您在评论区留言!感谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值