java浅拷贝与深拷贝(复制)-clone

目录

一、 值传递和引用传递
二、引用传递的作用
三、浅拷贝与深拷贝
四、数组深拷贝
五、List<String>深拷贝
六、List<Object>深拷贝
七、Map<String, String>深拷贝
八、Map<String, List<Map<String, Object>>>深拷贝
九、使用fastjson序列化深拷贝
十、使用SerializationUtils深拷贝

一、 值传递和引用传递

值传递:

以int类型为例,我们将a传入到方法changNumber中,在changNumber中将a修改为2。我们发现修改前后a都是一样的。

public class Maintest {
    public static void main(String[] args) {
        int a = 1;
        System.out.println(a);//修改前输出 1
        changNumber(a);
        System.out.println(a);//修改后输出 1
    }

    public static void changNumber(int a) {
        a = 2;
    }
}

引用传递:

以MyInfo对象为例,我们将age设置为10传入到方法changAge中,在changAge中将age设置为20。我们发现修改前后age不一样了。

public class MyInfo {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class Maintest {
    public static void main(String[] args) {
        MyInfo myinfo = new MyInfo();
        myinfo.setAge(18);
        System.out.println(myinfo.getAge()); //修改前输出 18
        changAge(myinfo);
        System.out.println(myinfo.getAge()); //修改后输出 20
    }

    public static void changAge(MyInfo myinfo) {
        myinfo.setAge(20);
    }
}

总结:

值传递:是将变量的值复制了一份进行传递,当复制的变量值改变了不会影响原本的变量值。比如: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呢,这就是我们下面要说的浅拷贝和深拷贝。

三、浅拷贝与深拷贝

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

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

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

比如,我有一个List<String>类型的 listA,我想把 listA 复制一份得到 listB ,对 listA 进行增加删除数据不会影响 listB,同样对 listB 操作也不会影响 listA,这就是深拷贝。

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

四、数组深拷贝

使用System.arraycopy拷贝基本数据类型的数组后,改变原始数组后,拷贝后的数组不会改变

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]

五、List<String>深拷贝

下面的方式支持 List<String> 、List<Integer> 、List<Double> 类型深拷贝

方式一:通过初始化 new 来复制:

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

 List<String> twoList = new ArrayList<>(oneList);
 //也可以像下面注释的代码使用流来拷贝
 //List<String> twoList =  oneList.stream().collect(Collectors.toList());

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

方法二:使用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]

方法三:使用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]

六、List<Object>深拷贝

1.简单List<Object>的拷贝(即类里只含有基本数据类型或包装类型,不含List、Map等,也不嵌套对象。):

示例:

Student类:

需重写Cloneable中的clone()方法

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

   //省略get、set方法
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "id:"+id+",name:"+name;
    }
    
    @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 = new ArrayList<>();
        for (Student s:studentsList){
            //循环取出每一个Student拷贝给一个临时的tempStudent,再add到studentsList1里
            Student tempStudent = null;
            try {
                tempStudent = (Student) s.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            studentsList1.add(tempStudent);
        }
        //测试,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:李四]
    }
}

以上代码可简化为:

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:李四]
    }

2.复杂List<Object>的拷贝(即类里含有List、Map或者嵌套其他对象。):

Address类:

public class Address implements Cloneable{
    public String type;
    public String value;

	//省略get、set方法

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


    @Override
    public String toString() {
        return "type:"+type+",value:"+value;
    }
}

Student类:

import java.util.List;

public class Student implements Cloneable {
    public Integer id;
    public String name;
    public List<Integer> score;
    public Address address;

   //省略get、set方法

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

    @Override
    public String toString() {
        return "id:"+id+",name:"+name+",score:"+score+",address:"+address;
    }
}
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);

        List<Student> studentsList1 = new ArrayList<>();

        //循环取出每一个Student拷贝给一个临时的tempStudent
        for (Student s : studentsList) {
            try {
            //循环取出每一个Student拷贝给一个临时的tempStudent
            Student tempStudent =   (Student) s.clone();
            //拷贝score到tempStudent里
            List<Integer> tempScore=tempStudent.getScore();
            List<Integer> score1 = new ArrayList<>();
            score1.addAll(tempScore);
            tempStudent.setScore(score1);
            //拷贝address到tempStudent里
            Address tempAddress=tempStudent.getAddress();
            Address address1 = (Address)tempAddress.clone();
            tempStudent.setAddress(address1);
            //tempStudent加到studentsList1里
            studentsList1.add(tempStudent);
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }

        //测试
        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);
    }
}

七、Map<String, String>深拷贝

Map<String, String> paramMap1 = new HashMap<>();
        paramMap1.put("name", "Marydon");
Map<String, String> paramMap2 = new HashMap<>();
paramMap2.putAll(paramMap1);
System.out.println(paramMap1);//输出 {name=Marydon}
System.out.println(paramMap2);//输出 {name=Marydon}

八、Map<String, List<Map<String, Object>>>深拷贝

将map深拷贝到map1

public class Maintest {
    public static void main(String[] args) {
        Map<String, Object> ins = new HashMap<>();
        ins.put("a-a",1);
        ins.put("a-b",2);
        Map<String, Object> ins1 = new HashMap<>();
        ins1.put("b-c",3);
        ins1.put("b-d",4);
        List<Map<String, Object>> list = new ArrayList<>();
        list.add(ins);
        list.add(ins1);

        Map<String, Object> ins2 = new HashMap<>();
        ins2.put("c-e",5);
        ins2.put("c-f",6);
        Map<String, Object> ins3 = new HashMap<>();
        ins3.put("d-g",7);
        ins3.put("d-h",8);
        List<Map<String, Object>> list1 = new ArrayList<>();
        list1.add(ins2);
        list1.add(ins3);

        Map<String, List<Map<String, Object>>> map = new HashMap<>();
        map.put("1",list);
        map.put("2",list1);

        Map<String, List<Map<String, Object>>> map1 = new HashMap<>();

        //遍历map获取list,再遍历list获取map,由内到外的拷贝
        for (Map.Entry<String, List<Map<String, Object>>> m: map.entrySet()){
            List<Map<String, Object>> value=  m.getValue();
            List<Map<String, Object>> value1 = new ArrayList<>();
            for(Map<String, Object> temmap:value){
                Map<String, Object> temmap1=new HashMap<>();
                temmap1.putAll(temmap);
                value1.add(temmap1);
            }
            map1.put(m.getKey(),value1);
        }

        //测试:改变map1里key是1的列表第0个元素的a-a为999
        map1.get("1").get(0).put("a-a",999);

        System.out.println(map);//{1=[{a-a=1, a-b=2}, {b-d=4, b-c=3}], 2=[{c-e=5, c-f=6}, {d-h=8, d-g=7}]}
        System.out.println(map1);//{1=[{a-a=999, a-b=2}, {b-d=4, b-c=3}], 2=[{c-f=6, c-e=5}, {d-h=8, d-g=7}]}
    }
}

九、使用fastjson序列化深拷贝

上面的上面的例子都是通过实现Cloneable类来实现拷贝,可以通过fastjson序列化来实现拷贝

示例1:

比如上面的【List<Object>深拷贝>】中的复杂list<Object>的拷贝使用fastjson实现拷贝更简单,而且使用fastjson拷贝,Student和Address类都不用实现Cloneable类也不需要重写clone方法:

import com.alibaba.fastjson.JSONObject;
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);

//开始序列化拷贝,将studentsList拷贝给studentsList1
        String listStr = JSONObject.toJSONString(studentsList);
        List<Student> studentsList1 = JSONObject.parseArray(listStr,Student.class);

//测试
        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);
    }   
}

示例2:

比如上面的【Map<String, List<Map<String, Object>>>深拷贝】使用fastjson实现拷贝更简单:

public class Maintest {
    public static void main(String[] args) {
        Map<String, Object> ins = new HashMap<>();
        ins.put("a-a",1);
        ins.put("a-b",2);
        Map<String, Object> ins1 = new HashMap<>();
        ins1.put("b-c",3);
        ins1.put("b-d",4);
        List<Map<String, Object>> list = new ArrayList<>();
        list.add(ins);
        list.add(ins1);

        Map<String, Object> ins2 = new HashMap<>();
        ins2.put("c-e",5);
        ins2.put("c-f",6);
        Map<String, Object> ins3 = new HashMap<>();
        ins3.put("d-g",7);
        ins3.put("d-h",8);
        List<Map<String, Object>> list1 = new ArrayList<>();
        list1.add(ins2);
        list1.add(ins3);

        Map<String, List<Map<String, Object>>> map = new HashMap<>();
        map.put("1",list);
        map.put("2",list1);

        String listStr = JSONObject.toJSONString(map);
        Map<String, List<Map<String, Object>>> map1 = (Map)JSON.parse(listStr);

        //测试:改变map1里key是1的列表第0个元素的a-a为999
        map1.get("1").get(0).put("a-a",999);

        System.out.println(map);//{1=[{a-a=1, a-b=2}, {b-d=4, b-c=3}], 2=[{c-e=5, c-f=6}, {d-h=8, d-g=7}]}
        System.out.println(map1);//{"1":[{"a-a":999,"a-b":2},{"b-d":4,"b-c":3}],"2":[{"c-e":5,"c-f":6},{"d-h":8,"d-g":7}]}
    }
}

注意:
如果Map的key和value是非字符串的数值类型时,深拷贝后的key类型会变为字符串,value会变为Integer类型。
比如下面的拷贝会有问题:

		Map<Long, Long> map = new HashMap<>();
        map.put(1L,2L);
        map.put(3L,4L);
        String string = JSONObject.toJSONString(map);
        //深拷贝为map1
        Map<Long, Long> map1 = (Map)JSON.parse(string);
        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());

拷贝后的map1的key实际类型是String,value实际类型是Integer

正确的拷贝应该如下:
使用TypeReference

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());

十、使用SerializationUtils深拷贝

除了上面的使用fastjson序列化实现深拷贝,还可以使用Apache Commons Lang3的SerializationUtils序列化实现深拷贝。

maven如下:

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

示例1:

比如上面的【List<Object>深拷贝>】中的复杂list<Object>的拷贝使用SerializationUtils实现拷贝更简单:
Student类和Address类都要实现Serializable。
Student类:

import java.io.Serializable;
import java.util.List;

public class Student implements Serializable {
    public Integer id;
    public String name;
    public List<Integer> score;
    public Address address;

   //省略get、set方法

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

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

Address类:

import java.io.Serializable;

public class Address implements Serializable {
    public String type;
    public String value;

    //省略get、set方法

    @Override
    public String toString() {
        return "type:"+type+",value:"+value;
    }
}

测试:

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);
    }
}

示例二:
比如上面的【Map<String, List<Map<String, Object>>>深拷贝】使用SerializationUtils实现拷贝更简单:

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

public class Maintest {
    public static void main(String[] args) {
        Map<String, Object> ins = new HashMap<>();
        ins.put("a-a",1);
        ins.put("a-b",2);
        Map<String, Object> ins1 = new HashMap<>();
        ins1.put("b-c",3);
        ins1.put("b-d",4);
        List<Map<String, Object>> list = new ArrayList<>();
        list.add(ins);
        list.add(ins1);

        Map<String, Object> ins2 = new HashMap<>();
        ins2.put("c-e",5);
        ins2.put("c-f",6);
        Map<String, Object> ins3 = new HashMap<>();
        ins3.put("d-g",7);
        ins3.put("d-h",8);
        List<Map<String, Object>> list1 = new ArrayList<>();
        list1.add(ins2);
        list1.add(ins3);

        Map<String, List<Map<String, Object>>> map = new HashMap<>();
        map.put("1",list);
        map.put("2",list1);

        Map<String, List<Map<String, Object>>> map1 = (Map) SerializationUtils.clone((Serializable)map);

        //测试:改变map1里key是1的列表第0个元素的a-a为999
        map1.get("1").get(0).put("a-a",999);

        System.out.println(map);//{1=[{a-a=1, a-b=2}, {b-d=4, b-c=3}], 2=[{c-e=5, c-f=6}, {d-h=8, d-g=7}]}
        System.out.println(map1);//{1=[{a-a=999, a-b=2}, {b-d=4, b-c=3}], 2=[{c-e=5, c-f=6}, {d-h=8, d-g=7}]}
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值