Java 浅拷贝和深拷贝 拷贝和复制

Java 复制与拷贝

大纲

image

浅拷贝和深拷贝

浅拷贝就是复制对象的一个精确副本

==对于基本数据类型就是复制值,对于引用数据类型而言就是复制对象地址==

深拷贝就是复制对象时

会给==引用数据类型生成一个新的空间==

image

浅拷贝

==基本数据类型不论是浅拷贝还是深拷贝都是传递值==

对于引用数据类型而言

浅拷贝只是拷贝了地址

一旦修改就会引起源数据的修改

==大多数对象的拷贝==一般都是浅拷贝

深拷贝

==基本数据类型不论是浅拷贝还是深拷贝都是传递值==

对于引用数据而言

深拷贝实实在在从新创建了一个内存区域存放一样的数据对象

这样修改是完全独立的

==深拷贝一般要自己实现==

一(二)维数组的拷贝

先看一个实验例子

public static void TlistClone(){
        
        System.out.println("一维数组(基本数据包装对象)的clone是克隆值  但是都是基本数据类型的所以值不同");
        Integer[]  d1= new Integer[]{1,1,2,3};
        Integer[]  d2 = d1.clone();
        d2[1]=Integer.valueOf(333);
        Arrays.stream(d1).forEach(integer -> System.out.print(integer+" "));
        System.out.println();
        Arrays.stream(d2).forEach(integer -> System.out.print(integer+" "));
        System.out.println();
        System.out.println("****************************************");
    
    
        System.out.println("一维数组(基本类型)的clone是克隆值 改变的基本数据类型的值");
        int[] a1 = new int[]{1,2,3};
        int[] a2 = a1.clone();
        a2[1]=444;
        System.out.println("源"+Arrays.toString(a1));
        System.out.println("拷贝"+Arrays.toString(a2));
        System.out.println("****************************************");
        System.out.println("一维数组的System.array(Arrays.copyOf())是克隆值  改变的也是基本数据类型的值");
        int[] b1 = new int[]{1,2,3};
        // Arrays.copy 底层是 System.arraycopy();
        int[] b2 = Arrays.copyOf(b1,b1.length);
        b2[1]=1110;
        System.out.println("源"+Arrays.toString(b1));
        System.out.println("拷贝"+Arrays.toString(b2));
        System.out.println("****************************************");
    
    
    
        System.out.println("二维数组的直接clone是浅拷贝,因为二维数组存储其中的一维数组的是地址所以值会同步修改");
        int[][] c1 = new int[][]{{1,2},{3,4}};
        int[][] c2 = c1.clone();
        c2[1][1]=2222;
        c2[0][0]=1;
        System.out.print("源");
        for (int i = 0; i < 2; i++) {
            System.out.println(Arrays.toString(c1[i]));
        }
        System.out.print("拷贝");
        for (int i = 0; i < 2; i++) {
            System.out.println(Arrays.toString(c2[i]));
        }
        System.out.println("****************************************");
    
    
    
        System.out.println("要二维数组正确clone全部值应该对二维数组每一行进行clone");
        int[][] c3 = new int[][]{{1,2},{3,4}};
        int[][] c4 = new int[2][2];
        for (int i = 0; i < 2; i++) {
            c4[i]=c3[i].clone();
        }
        System.out.print("源");
        for (int i = 0; i < 2; i++) {
            System.out.println(Arrays.toString(c3[i]));
        }
        System.out.print("拷贝");
        for (int i = 0; i < 2; i++) {
            System.out.println(Arrays.toString(c4[i]));
        }
        System.out.println("这样才正确");
    }

结果如下

一维数组(基本数据包装对象)的clone是克隆值  但是都是基本数据类型的所以值不同
1 1 2 3 
1 333 2 3 
****************************************
一维数组(基本类型)的clone是克隆值 改变的基本数据类型的值
源[1, 2, 3]
拷贝[1, 444, 3]
****************************************
一维数组的System.array(Arrays.copyOf())是克隆值  改变的也是基本数据类型的值
源[1, 2, 3]
拷贝[1, 1110, 3]
****************************************
二维数组的直接clone是浅拷贝,因为二维数组存储其中的一维数组的是地址所以值会同步修改
源[1, 2]
[3, 2222]
拷贝[1, 2]
[3, 2222]
****************************************
要二维数组正确clone全部值应该对二维数组每一行进行clone
源[1, 2]
[3, 4]
拷贝[1, 2]
[3, 4]
这样才正确

对象的拷贝

对象stu类

class Stu implements Cloneable,Serializable{
    // 实现Cloneable是为了clone方法 ,实现Serializable是为了序列化对象
    int id;
    String name;
    Location location;
    public Stu(){
        super();
    }
    public Stu(int id, String name,Location location) {
        this.id = id;
        this.name = name;
        this.location =location;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 对location进行深拷贝需要传入location
        Stu stu = (Stu) super.clone();
        Location location1 = (Location) stu.getLocation().clone();
        stu.setLocation(location1);
        return stu;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }
    @Override
    public String toString() {
        return "Stu{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", location=" + location +
                '}';
    }
}

对象location 类

class Location implements Cloneable{
    String city;

    public Location(String city) {
        this.city = city;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Location{" +
                "city='" + city + '\'' +
                '}';
    }

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

测试类

public static void ObejctClone() throws CloneNotSupportedException {

        System.out.println("****************************************");
        System.out.println("对象的clone(实现Cloneable接口)是默认浅拷贝(即实现接口但是不重写) 引用数据类型会被同步修改了");
        System.out.println("只有重写stu clone 方法的时候传入location的拷贝才可以 这里演示的是正确传入了location的拷贝");
        Location location1 = new Location("格陵兰岛");
        Stu stu1 = new Stu(1,"小明",location1);
        Stu stu2 = (Stu) stu1.clone();
        stu2.setId(2);
        stu2.setName("小红");
        Location location2 = stu2.getLocation();
        location2.setCity("中国");
        System.out.println(stu1);
        System.out.println(stu2);
        System.out.println("重写之后(添加对location的拷贝 两个location不一样了");
        System.out.println("****************************************");


        System.out.println("对象数组clone方法是浅拷贝");
        Stu[] stus1 = new Stu[]{stu1,stu2};
        Stu[] stus2 = stus1.clone();
        System.out.println("原来");
        Arrays.stream(stus1).forEach(stu -> System.out.print(stu+" "));
        System.out.println();
        Arrays.stream(stus2).forEach(stu -> System.out.print(stu+" "));
        System.out.println();
        System.out.println("更改后");
        stus1[0].setLocation(new Location("日本"));
        stus1[0].setName("小车");
        Arrays.stream(stus1).forEach(stu -> System.out.print(stu+" "));
        System.out.println();
        Arrays.stream(stus2).forEach(stu -> System.out.print(stu+" "));
    }

结果

****************************************
对象的clone(实现Cloneable接口)是默认浅拷贝(即实现接口但是不重写) 引用数据类型会被同步修改了
只有重写stu clone 方法的时候传入location的拷贝才可以 这里演示的是正确传入了location的拷贝
Stu{id=1, name='小明', location=Location{city='格陵兰岛'}}
Stu{id=2, name='小红', location=Location{city='中国'}}
重写之后(添加对location的拷贝 两个location不一样了
****************************************
对象数组clone方法是浅拷贝
原来
Stu{id=1, name='小明', location=Location{city='格陵兰岛'}} Stu{id=2, name='小红', location=Location{city='中国'}} 
Stu{id=1, name='小明', location=Location{city='格陵兰岛'}} Stu{id=2, name='小红', location=Location{city='中国'}} 
更改后
Stu{id=1, name='小车', location=Location{city='日本'}} Stu{id=2, name='小红', location=Location{city='中国'}} 
Stu{id=1, name='小车', location=Location{city='日本'}} Stu{id=2, name='小红', location=Location{city='中国'}} 

集合对象的拷贝

public static void ColletionClone() throws IOException, ClassNotFoundException {
    System.out.println("Colletions.copy方法是浅拷贝");
    List<Stu> list = new ArrayList<>();
    list.add(new Stu(1,"a",null));
    list.add(new Stu(2,"b",null));
    List<Stu> list2 = new ArrayList<>();
    list2.add(null);
    list2.add(null);
    Collections.copy(list2,list);
    list.get(1).setName("C");
    System.out.println(list);
    System.out.println(list2);
    System.out.println("************************************");
    System.out.println("addAll方法是浅拷贝");
    List<Stu> liststudent = new ArrayList<>();
    liststudent.add(new Stu(1,"a",null));
    liststudent.add(new Stu(2,"b",null));
    List<Stu> liststudent2 = new ArrayList<>();
    liststudent2.addAll(liststudent);
    liststudent.get(1).setName("C");
    System.out.println(liststudent);
    System.out.println(liststudent2);
    System.out.println("************************************");
    System.out.println("构造方法传入是浅拷贝");
    List<Stu> listcon = new ArrayList<>();
    listcon.add(new Stu(1,"a",null));
    listcon.add(new Stu(2,"b",null));
    List<Stu> listcon2 = new ArrayList<>(listcon);
    listcon.get(1).setName("C");
    System.out.println(listcon);
    System.out.println(listcon2);
    System.out.println("************************************");
    System.out.println("序列化 是深拷贝");
    List<Stu> listserial = new ArrayList<>();
    listserial.add(new Stu(1,"a",null));
    listserial.add(new Stu(2,"b",null));
    List<Stu> listseria2;
    listseria2=deepCopy(listserial);
    listserial.get(1).setName("C");
    System.out.println(listserial);
    System.out.println(listseria2);
}

序列化对象的方法

public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {

    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(byteOut);
    out.writeObject(src);

    ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
    ObjectInputStream in = new ObjectInputStream(byteIn);
    @SuppressWarnings("unchecked")
    List<T> dest = (List<T>) in.readObject();
    return dest;
}

结果:

Colletions.copy方法是浅拷贝
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
************************************
addAll方法是浅拷贝
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
************************************
构造方法传入是浅拷贝
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
************************************
序列化 是深拷贝
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
[Stu{id=1, name='a', location=null}, Stu{id=2, name='b', location=null}]

总结

对于基本数据类型而言

==没有深拷贝和浅拷贝==,因为都是要修改实际的值

对于引用对象类型

==一般的方法都是浅拷贝==

==只有正确重写了clone方法或者序列化了才是深拷贝==

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • java中传递为值传递,对于引用类型,改变值后,原对象及引用对象值将都改变,最近刚好看java中浅拷贝和深拷贝问题...
    周文冬阅读 272评论 0 1
  • 介绍 开发过程中,有时会遇到把现有的一个对象的所有成员属性拷贝给另一个对象的需求。比如说对象 A 和对象 B,二者...
    安静的蓝孩子阅读 74,294评论 12 61
  • 写在前面 各类技术论坛关于深拷贝的博客有很多,有些写的也比我好,那为什么我还要坚持写这篇博客呢,之前看到的一篇博客...
    心_c2a2阅读 21,130评论 3 18
  • 307、setValue:forKey和setObject:forKey的区别是什么? 答:1, setObjec...
    AlanGe阅读 1,523评论 0 1
  • Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。...
    萨达哈鲁酱阅读 927评论 0 3