Java中深度克隆和浅度克隆

Wesley13
• 阅读 541

一:使用目的:

就是为了快速构造一个和已有对象相同的副本。如果需要克隆对象,一般需要先创建一个对象,然后将原对象中的数据导入到新创建的对象中去,而不用根据已有对象进行手动赋值操作。

二:Object中的clone()方法

protected native Object clone() throws CloneNotSupportedException;

  说明:1.这是一个navtive方法  2.要使用该方法必须继承Object类,因为修饰符为protected  3.返回值为Object,需要强转

  使用该方法时:x.clone()!=x为true,对于基础类型来说,在堆内存中创建了一个独立且内容与之相同的内存区域.对于引用数据类型来说,克隆对象和原始对象在java 堆(heap)中是两个独立的对  象,x.clone().getClass() == x.getClass()  他们所属的类是同一个,x.clone().equals(x)   所比较的对象内容相同

三:深度克隆和浅度克隆

浅度克隆:被克隆得到的对象基本类型的值修改了,原对象的值不会改变

深度克隆:被克隆得到的对象基本类型的值修改了,原对象的值改变

public class ShadowClone implements Cloneable {    private int a;   // 基本类型    private int[] b; // 非基本类型    // 重写Object.clone()方法,并把protected改为public    @Override    public Object clone(){        ShadowClone sc = null;        try        {            sc = (ShadowClone) super.clone();        } catch (CloneNotSupportedException e){            e.printStackTrace();        }        return sc;    }    public int getA()    {        return a;    }    public void setA(int a)    {        this.a = a;    }    public int[] getB() {        return b;    }    public void setB(int[] b) {        this.b = b;    }}

ShadowClone c1 = new ShadowClone();//对c1赋值c1.setA(100) ;c1.setB(new int[]{1000}) ;System.out.println("克隆前c1:  a="+c1.getA()+" b="+c1.getB()[0]);//克隆出对象c2,并对c2的属性A,B,C进行修改ShadowClone c2 = (ShadowClone) c1.clone();//对c2进行修改c2.setA(50) ;int []a = c2.getB() ;a[0]=500 ;c2.setB(a);System.out.println("克隆前c1:  a="+c1.getA()+" b="+c1.getB()[0]);System.out.println("克隆后c2:  a="+c2.getA()+ " b[0]="+c2.getB()[0]);

console:

克隆前c1: a=100 b=1000
克隆前c1: a=100 b=500
克隆后c2: a=50 b[0]=500

可见:基本类型可以使用浅克隆,而对于引用类型,由于引用的是内容相同,所以改变c2实例对象中的属性就会影响到c1。所以引用类型需要使用深克隆。另外,在开发一个不可变类的时候,如果这个不可变类中成员有引用类型,则就需要通过深克隆来达到不可变的目的。

四:重写clone方法完成深度克隆

class bottle implements Cloneable {
    public wine wn;

    public bottle(wine wn) {
        this.wn = wn;
    }
    // 覆写clone()方法
    protected Object clone() throws CloneNotSupportedException {
        bottle newBtl = (bottle) super.clone();
        newBtl.wn = (wine) wn.clone();
        return newBtl;
    }
}

class wine implements Cloneable {
    int degree;
    public int getDegree() {
        return degree;
    }
    public void setDegree(int degree) {
        this.degree = degree;
    }
    // 覆写clone()方法
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public static void main(String[] args) throws CloneNotSupportedException {    bottle bottle = new bottle(new wine());    bottle bottle1 = (bottle) bottle.clone();    System.out.println("bottle1.wine : " + bottle1.wn.getDegree() );    bottle1.wn.setDegree(100);    System.out.println("bottle1.wine : " + bottle1.wn.getDegree() );    System.out.println("bottle.wine : " + bottle.wn.getDegree());}

console:

bottle1.wine : 0
bottle1.wine : 100
bottle.wine : 0

如果wine类中多了一个String name的属性呢?

class wine implements Cloneable {
    int degree;
    String name="法国白兰地";

    public int getDegree() {
        return degree;
    }
    public void setDegree(int degree) {
        this.degree = degree;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    // 覆写clone()方法
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class bottle implements Cloneable {
    public wine wn;

    public bottle(wine wn) {
        this.wn = wn;
    }
    // 覆写clone()方法
    protected Object clone() throws CloneNotSupportedException {
        bottle newBtl = (bottle) super.clone();
        newBtl.wn = (wine) wn.clone();
        return newBtl;
    }
}

Test

     bottle bottle = new bottle(new wine());
        bottle bottle1 = (bottle) bottle.clone();
        
        System.out.println("bottle1.wine : " + bottle1.wn.getName() );
        bottle1.wn.setName("中国二锅头");
        
        System.out.println("bottle1.wine : " + bottle1.wn.getName() );
        System.out.println("bottle.wine : " + bottle.wn.getName());

console:

bottle1.wine : 法国白兰地
bottle1.wine : 中国二锅头
bottle.wine : 法国白兰地   

五:使用序列化实现深度克隆

public class DeepPerson implements Serializable {
    private int a;
    private int[] b;

    public DeepPerson() {
    }

    public DeepPerson(int a, int[] b) {
        this.a = a;
        this.b = b;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int[] getB() {
        return b;
    }

    public void setB(int[] b) {
        this.b = b;
    }
}

public class Test1 {

    public static void main(String[] args) throws CloneNotSupportedException{
        DeepPerson dc1 = new DeepPerson();
        // 对dc1赋值
        dc1.setA(100);
        dc1.setB(new int[] { 1000 });
        System.out.println("克隆前dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]);
        DeepPerson dc2 = (DeepPerson) deepClone(dc1);
        // 对c2进行修改
        dc2.setA(50);
        int[] a = dc2.getB();
        a[0] = 500;
        System.out.println("克隆后dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]);
        System.out.println("克隆后dc2: a=" + dc2.getA()+"b[0]=" + dc2.getB()[0]);
    }
    
    public static Object deepClone(Object object){
        Object o=null;
        try{
            if (object != null){
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(object);
                oos.close();
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                o = ois.readObject();
                ois.close();
            }
        } catch (IOException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return o;
    }
}

console:

克隆前dc1: a=100b[0]=1000
克隆后dc1: a=100b[0]=1000
克隆后dc2: a=50b[0]=500

六、总结:

1.克隆方法用于创建对象的拷贝,为了使用clone方法,类必须实现java.lang.Cloneable接口重写protected方法clone,如果没有实现Clonebale接口会抛出CloneNotSupportedException.

2.在克隆java对象的时候不会调用构造器
3.java提供一种叫浅拷贝(shallow copy)的默认方式实现clone,创建好对象的副本后然后通过赋值拷贝内容,意味着如果你的类包含引用类型,那么原始对象和克隆都将指向相同的引用内容,这是很危险的,因为发生在可变的字段上任何改变将反应到他们所引用的共同内容上。为了避免这种情况,需要对引用的内容进行深度克隆。
4.按照约定,实例的克隆应该通过调用super.clone()获取,这样有助克隆对象的不变性。如:clone!=original和clone.getClass()==original.getClass(),尽管这些不是必须的

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
Karen110 Karen110
2年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
灯灯灯灯 灯灯灯灯
2年前
「超全超细」Java设计模式图文详解!!!
java设计模式—原型模式Java原型模式1、概述  啥是原型模式?  原型模式属于设计模式中的创建型中的一员,  原型模式:使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象!  说大白话就是自己复制自己,通过原生对象复制出一个新的对象,这两个对象结构相同且相似;  需要注意的是,原型对象自己不仅是个对象还是个工厂!并且通过克隆方式创
Wesley13 Wesley13
2年前
java8新特性
Stream将List转换为Map,使用Collectors.toMap方法进行转换背景:User类,类中分别有id,name,age三个属性。List集合,userList,存储User对象1、指定keyvalue,value是对象中的某个属性值。 Map<Integer,StringuserMap1userList.str
Wesley13 Wesley13
2年前
Java深拷贝和浅拷贝
1.浅复制与深复制概念⑴浅拷贝(浅克隆)   复制出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。⑵深拷贝(深克隆)   复制出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,而不再是原有的那些被引用的对象。换言之,深复制
Wesley13 Wesley13
2年前
Java中如何克隆集合——ArrayList和HashSet深拷贝
编程人员经常误用各个集合类提供的拷贝构造函数作为克隆List,Set,ArrayList,HashSet或者其他集合实现的方法。需要记住的是,Java集合的拷贝构造函数只提供浅拷贝而不是深拷贝,这意味着存储在原始List和克隆List中的对象是相同的,指向Java堆内存中相同的位置。增加了这个误解的原因之一是对于不可变对象(https:/
Wesley13 Wesley13
2年前
unity将 -u4E00 这种 编码 转汉字 方法
 unity中直接使用 JsonMapper.ToJson(对象),取到的字符串,里面汉字可能是\\u4E00类似这种其实也不用转,服务器会通过类似fastjson发序列化的方式,将json转对象,获取对象的值就是中文但是有时服务器要求将传参中字符串中类似\\u4E00这种转汉字,就需要下面 publ
Wesley13 Wesley13
2年前
Java原型模式
原型模式  原型模式也称克隆模式。原型模式jianmingzhiyi,就是先创造出一个原型,然后通过类似于Java中的clone方法,对对象的拷贝,克隆类似于new,但是不同于new。new创造出来的对象采用的是默认值。克隆出来的对象与原型对象相同,同时不会影响原型对象,然后在修改克隆出来的对象。实现  继承Cloneable接口,重写cl
Stella981 Stella981
2年前
Clone
Java中对象的创建clone顾名思义就是复制,在Java语言中,clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。那么在java语言中,有几种方式可以创建对象呢?1使用new操作符创建一个对象2使用clone方法复制一个对象那么这两种方