java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java clone,sallow copy和deep copy区别

Java中的clone,sallow copy和deep copy的区别解析

作者:杨DaB

本文给大家介绍了Java中的clone,sallow copy和deep copy的区别解析,介绍了克隆的概念和作用,以及浅克隆和深克隆的区别,感兴趣的朋友跟随小编一起看看吧

克隆的概念和作用

  1. 方法需要 return 引用类型,但又不希望自己本身 持有引用类型的对象被修改。
  2. 程序之间方法的调用时参数的传递。有些场景为了保证引用类型的参数不被其他方法修改,可以使用克隆后的值作为参数传递。

sallow copy和deep copy的区别

sallow copy(浅克隆)

两个对象使用的引用类型的属性会互相影响

deep copy(深克隆)

如何实现deep copy

  1. 实现 Cloneable 接口,重写 clone() 方法。
  2. 不实现 Cloneable 接口,会报 CloneNotSupportedException 异常。
  3. Objectclone() 方法是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。

方法1、对象的属性的Class 也实现 Cloneable 接口,克隆对象的同时也克隆属性。

@Override
public Object clone() throws CloneNotSupportedException {
    DPerson p = (DPerson)super.clone();
    p.setFood((DFood)p.getFood().clone());
    return p;
}

如下:

  1. DPerson 类实现 Cloneable接口,重写clone()方法,将对象 和属性全部clone;
  2. DPerson 引用的对象Food类 实现Cloneable接口,重写clone()方法;
  3. 在主程序中调用DPerson 中的clone()方法,更改克隆后的对象内的属性进行测试
/**
 * 测试克隆
 */
public class TestManalDeepClone {
    public static void main(String[] args) throws Exception {
        DPerson p1 = new DPerson(1, "AKA000", new DFood("米饭"));//创建Person 对象 p1
        DPerson p2 = (DPerson)p1.clone();//克隆p1
        p2.setName("AKA121");//修改p2的name属性
        p2.getFood().setName("面条");//修改p2的自定义引用类型 food 属性
        System.out.println(p1);//修改p2的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性也随之改变,说明p2的food属性,只拷贝了引用,没有拷贝food对象
        System.out.println(p2);
    }
}
class DPerson implements Cloneable {
    private int pid;
    private String name;
    private DFood food;
    public DPerson(int pid, String name, DFood food) {
        this.pid = pid;
        this.name = name;
        this.food = food;
        System.out.println("Person constructor call");
    }
    public int getPid() {
        return pid;
    }
    public void setPid(int pid) {
        this.pid = pid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        DPerson p = (DPerson)super.clone();
        p.setFood((DFood)p.getFood().clone());
        return p;
    }
    @Override
    public String toString() {
        return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
    }
    public DFood getFood() {
        return food;
    }
    public void setFood(DFood food) {
        this.food = food;
    }
}
class DFood implements Cloneable{
    private String name;
    public DFood(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

方法2、结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝

手写一个方法 cloneBySerizalizable(),使用 ByteStream流和ObjectSteam流实现;

    /**
     * 通过序列化完成克隆
     * @return
     */
    public Object cloneBySerializable() {
        Object obj = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            obj = ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return obj;
    }

如下:

  1. 整体思想:将数据序列化成为字节流,再反序列化为数据行程对象的clone,做出一个全新的、与原始对象完全独立的对象。;
  2. 编写cloneBySerializable()方法,创建一个Object对象,设置为成员变量初始化为null,做传输使用;
  3. 创建 ByteArrayOutputStream 容器,用于接收和存储后续写入的字节数据
  4. 创建一个对象输出流 ObjectOutputStream oos = new ObjectOutputStream(baos); ,包装在 ByteArrayOutputStream 之上,不能独立存在,必须依赖一个底层的输出流,指定数据最终的去向。
  5. 使用writeObject 方法,将当前对象 (this) 序列化为字节流。这些字节数据被写入到 ByteArrayOutputStream这个内存容器中。
  6. ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 将对象反序列化会bais中,在通过 ObjectInputStream 进行读取, ObjectInputStream 必须包装在 ByteArrayInputStream 之中;
  7. 通过readObject 方法,从字节流中读取数据并反序列化,最终创建出一个全新的对象。
  8. 在主程序中调用DPerson 中的cloneBySerializable()方法,更改克隆后的对象内的属性进行测试
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class TestSeriazableClone {
    public static void main(String[] args) {
        SPerson p1 = new SPerson(1, "AKA000", new SFood("米饭"));//创建 SPerson 对象 p1
        SPerson p2 = (SPerson)p1.cloneBySerializable();//克隆 p1
        p2.setName("AKA001");//修改 p2 的 name 属性
        p2.getFood().setName("面条");//修改 p2 的自定义引用类型 food 属性
        System.out.println(p1);//修改 p2 的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性未随之改变,说明p2的food属性,只拷贝了引用和 food 对象
        System.out.println(p2);
    }
}
class SPerson implements Cloneable, Serializable {
    private static final long serialVersionUID = -7710144514831611031L;
    private int pid;
    private String name;
    private SFood food;
    public SPerson(int pid, String name, SFood food) {
        this.pid = pid;
        this.name = name;
        this.food = food;
        System.out.println("Person constructor call");
    }
    public int getPid() {
        return pid;
    }
    public void setPid(int pid) {
        this.pid = pid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    /**
     * 通过序列化完成克隆
     * @return
     */
    public Object cloneBySerializable() {
        Object obj = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            obj = ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return obj;
    }
    @Override
    public String toString() {
        return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
    }
    public SFood getFood() {
        return food;
    }
    public void setFood(SFood food) {
        this.food = food;
    }
}
class SFood implements Serializable {
    private static final long serialVersionUID = -3443815804346831432L;
    private String name;
    public SFood(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

到此这篇关于Java中的clone,sallow copy和deep copy的区别解析的文章就介绍到这了,更多相关java clone,sallow copy和deep copy区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文