JAVA-面向对象之对象拷贝

Java 中的数据类型分为基本数据类型和引用数据类型。对于这两种数据类型,在进行赋值操作、用作方法参数或返回值时,会有值传递和引用(地址)传递的差别。

Map对象

测试01-等号赋值:

@Test
    public void TestCopy() {
        Map<String, Object> beginByMap = new HashMap<>();
        beginByMap.put("key01", "Value01");
        Map<String, Object> endByMap = beginByMap;
        endByMap.put("key02", "Value02");
        System.out.println("beginByMap:" + beginByMap);
        System.out.println("endByMap:" + endByMap);
    }

image
对象复制或者说拷贝在等号下的引用类型是浅拷贝:{拷贝分为浅拷贝和深度拷贝}

可以看出 对象的 = 号赋值时 是浅复制,两个对象实际引用的是同一个地址,所以不管谁改变了,都会变。。

有时候我们又需要两个对象相等但是互相操作不干扰

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。
(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。

我这里JDK1.8

结果是:不管使用MAP的PUTALLH还是HashMap的PutAll都是属于深度拷贝
但是网上一部分人说的是HashMap才是深度拷贝,而MAp的不是深度拷贝,这个有待考察


    @Test
    public void TestCopy() {
        Map<String, Object> beginByMap = new HashMap<>();
        beginByMap.put("key01", "ADC");
        //使用浅拷贝
        Map<String, Object> endByMap = new HashMap<>();
        endByMap.putAll(beginByMap);
        endByMap.put("key02", "ABC");
        endByMap.put("key03", "ADA");
        endByMap.remove("key01");
        System.out.println("beginByMap:" + beginByMap);
        System.out.println("endByMap:" + endByMap);

        HashMap<String, Object> b = new HashMap<>();
        b.putAll(beginByMap);
        b.put("key02", "ADA");
        b.put("key03", "ADA");
        b.remove("key01");
        System.out.println(b);
        System.out.println(beginByMap);
    }

image

List对象

几种常见的浅拷贝:

1.直接用“=”赋值

2.遍历循环复制

3.使用List实现类的构造方法

4.使用list.addAll()方法

list的深拷贝

LIST<泛型> 泛型一定是Object类型,,实现cloneable接口并且重写clone方法。
clone方法:就是深度拷贝更多的如下

public class MyTest {


    @Test
    public void TestCopy() throws Exception {
        List<Person> list1 = new ArrayList<Person>();

        list1.add(new Person("aaa"));
        list1.add(new Person("bbb"));
        
        
        
        // 第一种方法:通过构造函数来实现,它里面调用了Arrays.copyOf(),copyOf又调用了System.arraycopy
        List<Person> list2 = new ArrayList<Person>(list1);
        list2.get(0).setName("ccc");

        System.out.println("list1 ==>" + list1);
        System.out.println("list2 ==>" + list2);
        /** 此时list1 和list2 输出相同,说明是引用*/
        // 第二种方法:addAll方法,它调用了System.arraycopy
        // System.arraycopy:源数据-->目的数据
        // Arrays.copyOf():源数据及长度,数据类型,它里面调用了System.arraycopy
        List<Person> list3 = new ArrayList<Person>();
        list3.addAll(list1);
        list3.get(0).setName("ddd");

        System.out.println("list1 ==>" + list1);
        System.out.println("list3 ==>" + list3);
        /** 此时list1 和list3 输出相同,说明是引用*/

        // 第三种方法:实现clone来实现
        List<Person> list4 = new ArrayList<Person>();
        for (int i = 0; i < list1.size(); i++) {
            list4.add(list1.get(i).clone());
        }
        list4.get(0).setName("eee");
        System.out.println("list1 ==>" + list1);
        System.out.println("list4 ==>" + list4);
        /** 此处clone了,但要注意实现了cloneable接口只能对原始类型和String进行clone,如果是引用类型,则要进行深clone,复制内容或者对象再次clone*/

        // 第四种方法:通过序列化来实现
        List<Person> list5 = null;
        OutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(list1);

        InputStream bi = new ByteArrayInputStream(((ByteArrayOutputStream) bo).toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        list5 = (List<Person>) oi.readObject();
        list5.get(0).setName("ggg");
        System.out.println("list1 ==>" + list1);
        System.out.println("list5 ==>" + list5);
        /** 通过这种方法也能实现深拷贝*/

        // 第五种方法: Collection.copy,它的实现过程是通过遍历然后set进入
        List<Person> list6 = new ArrayList<Person>(Arrays.asList(new Person[list1.size()]));

        Collections.copy(list6, list1);
        list6.get(0).setName("hhh");
        System.out.println("list1 ==>" + list1);
        System.out.println("list6 ==>" + list6);
        /** list1 和list6 输出结果是相同的*/
    }

}

class Person implements Cloneable, Serializable {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return this.name;
    }

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

    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);
        return (List<T>)in.readObject();
    }

}


posted on 2021-06-30 17:34  白嫖老郭  阅读(478)  评论(0编辑  收藏  举报

导航