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 list = new ArrayList<>();
    list.add(new Stu(1,"a",null));
    list.add(new Stu(2,"b",null));
    List 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 liststudent = new ArrayList<>();
    liststudent.add(new Stu(1,"a",null));
    liststudent.add(new Stu(2,"b",null));
    List 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 listcon = new ArrayList<>();
    listcon.add(new Stu(1,"a",null));
    listcon.add(new Stu(2,"b",null));
    List listcon2 = new ArrayList<>(listcon);
    listcon.get(1).setName("C");
    System.out.println(listcon);
    System.out.println(listcon2);
    System.out.println("************************************");
    System.out.println("序列化 是深拷贝");
    List listserial = new ArrayList<>();
    listserial.add(new Stu(1,"a",null));
    listserial.add(new Stu(2,"b",null));
    List listseria2;
    listseria2=deepCopy(listserial);
    listserial.get(1).setName("C");
    System.out.println(listserial);
    System.out.println(listseria2);
}

序列化对象的方法

public static  List deepCopy(List 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 dest = (List) 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方法或者序列化了才是深拷贝==

你可能感兴趣的:(Java 浅拷贝和深拷贝 拷贝和复制)