java 深拷贝和浅拷贝

1.值传递和引用传递

值传递:是将变量的值复制了一份进行传递,当复制的变量值改变了不会影响原本的变量值。比如:int、double、String、Integer、Double等基本类型以及包装类都是值传递。

引用传递:引用传递一般是对于对象型变量而言的,传递的是内存中对象的地址,所以传递后的内容改变了原本的内容也会改变。比如:List、Map、对象等都是引用传递。

对于基本类型传递的是值,对于引用类型传递的是对象的内存地址的值,所以有时候你会看到有的教程说Java里只有一种方式就是值传递,这里要理解它的意思。

例如:changList可以不返回值就修改list,比如可以在changList中写对list的新增、删除、排序等,main方法中的list也会跟着被修改

public class Maintest {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        System.out.println(list);// 输出 [1]
        changList(list);
        System.out.println(list);// 输出 [1, 2]
    }
    
    public static void changList(List<Integer> list) {
        list.add(2);
    }
}

那万一我们想复制一份list数据传递到changList方法中,changList的list改变了而又不想改变main方法中的list呢,这就是我们下面要说的浅拷贝和深拷贝。

浅拷贝和深拷贝又被叫做浅复制和深复制,浅拷贝与深拷贝都是针对对象来说的。

浅拷贝:复制了一个对象,改变其中一个另外一个也会跟着改变,也就是上面说的引用传递。

深拷贝:复制了一个对象,两者完全隔离,也就是说改变其中一个另一个不受影响。

浅拷贝就像上面引用传递示例那样,本文后面主要讲的都是深拷贝。

 

如何实现深拷贝:

1.数组深拷贝

int[] a1 = {1, 2, 3, 4, 5};
int[] a2 = new int[a1.length];

System.arraycopy(a1, 0, a2, 0, a1.length);//System.arraycopy参数说明:(原数组, 原数组的开始位置, 目标数组, 目标数组的开始位置, 拷贝个数)
System.out.println(Arrays.toString(a1)); // [1, 2, 3, 4, 5]
System.out.println(Arrays.toString(a2)); // [1, 2, 3, 4, 5]

2. LIst<String>深拷贝

(1)java8以后可以用steam

 List<String> oneList = new ArrayList<String>();
 oneList.add("1");
 oneList.add("2");
 oneList.add("3");

 List<String> twoList =  oneList.stream().collect(Collectors.toList());

 System.out.println(oneList);//[1, 2, 3]
 System.out.println(twoList);//[1, 2, 3]

(2)使用addAll

List<String> oneList = new ArrayList<String>();
oneList.add("1");
oneList.add("2");
oneList.add("3");

List<String> twoList = new ArrayList<String>();
twoList.addAll(oneList);

System.out.println(oneList);//[1, 2, 3]
System.out.println(twoList);//[1, 2, 3]

(3)使用Collections.copy

List<String> oneList = new ArrayList<>();
oneList.add("1");
oneList.add("2");
oneList.add("3");

List<String> twoList = new ArrayList<>(Arrays.asList(new String[oneList.size()]));//twoList要初始化大小
Collections.copy(twoList, oneList);
        
System.out.println(oneList);//[1, 2, 3]
System.out.println(twoList);//[1, 2, 3]

 

3.List<Object>深拷贝

例,Student类如下,实现了 Cloneable接口,并重写clone方法:

public class Student implements Cloneable {
    public Integer id;
    public String name;

   //省略get、set方法

    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String toString() {
        return "id:" + id + ",name:" + name;
    }
}

深拷贝实现:

public class Maintest {
    public static void main(String[] args) {
        Student student = new Student();
        student.setId(1);
        student.setName("张三");
        Student student1 = new Student();
        student1.setId(2);
        student1.setName("李四");

        List<Student> studentsList = new ArrayList<>();
        studentsList.add(student);
        studentsList.add(student1);
        //克隆
        List<Student> studentsList1 = studentsList.stream().map(v ->(Student) v.clone()).collect(Collectors.toList());
        //测试,studentsList里张三的i的
        studentsList.get(0).setId(99);
        System.out.println(studentsList); //[id:99,name:张三, id:2,name:李四]
        System.out.println(studentsList1); //[id:1,name:张三, id:2,name:李四]
    }

 

4.我推荐直接使用fastjson序列化深拷贝

Map<Long, Long> map = new HashMap<>();
        map.put(1L,2L);
        map.put(3L,4L);
        String string = JSONObject.toJSONString(map);
        Map<Long, Long> map1 = JSON.parseObject(string,new TypeReference<Map<Long,Long>>(){});
        
        List<Long> keysList = map1.entrySet().stream()
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        List<Long> valuesList = map1.entrySet().stream()
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());
        System.out.println("class " + keysList.get(0).getClass());
        System.out.println("class " + valuesList.get(0).getClass());

5.我推荐使用SerializationUtils进行深拷贝,前提是当前类实现了Serializable接口,入HashMap

maven如下:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

实现如下:

import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Maintest {
    public static void main(String[] args) {
        List<Integer> sco=new ArrayList<>();
        sco.add(90);
        Address addre = new Address();
        addre.setType("111");
        addre.setValue("home");

        List<Integer> sco1=new ArrayList<>();
        sco1.add(80);
        Address addre1 = new Address();
        addre1.setValue("home");
        addre1.setType("222");

        //student赋值
        Student student = new Student();
        student.setId(1);
        student.setName("张三");
        student.setScore(sco);
        student.setAddress(addre);

        //student1赋值
        Student student1 = new Student();
        student1.setId(2);
        student1.setName("李四");
        student1.setScore(sco1);
        student1.setAddress(addre1);

        List<Student> studentsList = new ArrayList<>();
        studentsList.add(student);
        studentsList.add(student1);
        
        //使用SerializationUtils拷贝
        List<Student> studentsList1 = (List<Student>)SerializationUtils.clone((Serializable)studentsList);

        //测试
        studentsList.get(0).getScore().set(0,10);
        studentsList.get(0).getAddress().setValue("office");

//输出:[id:1,name:张三,score:[10],address:type:111,value:office, id:2,name:李四,score:[80],address:type:222,value:home]
        System.out.println(studentsList);

//输出[id:1,name:张三,score:[90],address:type:111,value:home, id:2,name:李四,score:[80],address:type:222,value:home]
        System.out.println(studentsList1);
    }
}

 

posted @ 2022-04-27 11:57  佩洛君  阅读(64)  评论(0编辑  收藏  举报