Java中的深拷贝和浅拷贝

 

Java中的深拷贝和浅拷贝

[TOC]

一、什么是深拷贝、浅拷贝

  • 浅拷贝:将被复制的对象的成员变量拷贝到新创建的对象的成员变量中,即
    • 对于基本类型来说,会直接进行值传递,也就是将该属性值复制一份给新的对象,也就是说新老对象的数据是不共享的,只是值相同而已
    • 对于引用类型来说,拷贝时也是把老对象的引用对象的地址拷贝到新对象中,实际上新老两个对象引用的是用一个对象
  • 深拷贝不仅对基本类型变量进行值赋值,而且对于引用类型会为新对象创建相应的新的引用类型,即新老对象引用的是不同的对象
  • 示意图:

img

二、如何实现浅拷贝

  • String类型属于引用数据类型,不属于基本数据类型,但是String类型的数据是存放在常量池中的,也就是无法修改的;

2.1通过构造方法实现

  • 通过创建一个使用该类对象作为参数的构造方法,能实现浅拷贝;

  • class Student{
        public int age;
        public String name;
        public Teacher teacher;
          
        public Student(int age, String name, Teacher teacher){
            this.age=age;
            this.name=name;
            this.teacher=teacher;
        }
        // 拷贝构造方法
        public Student(Student s){
            this.age=s.age;
            this.name=s.name;
            this.teacher=s.teacher;
        }
          
    }
      
    class Teacher{
        public int age;
        public String name;
          
        public Teacher(int age, String name){
            this.age=age;
            this.name=name;
        }
    }
      
    public class ShallowCopy {
      
        public static void main(String[] args) {
            Teacher t1 = new Teacher(30,"zyn");
            Student s1 = new Student(20,"kevin",t1);
            Student s2 = new Student(s1);
      
            System.out.println("s1是"+s1.name+", "+s1.age+",s1的老师为 "+s1.teacher.name+", "+s1.teacher.age);
            System.out.println("s2是"+s2.name+", "+s2.age+",s2的老师为 "+s2.teacher.name+", "+s2.teacher.age);
      
            s1.age=25;
            s1.name="kevin11";
            s1.teacher.age=40;
            System.out.println("修改s1的name、age、teacher后");
      
            System.out.println("s1是"+s1.name+", "+s1.age+",s1的老师为 "+s1.teacher.name+", "+s1.teacher.age);
            System.out.println("s2是"+s2.name+", "+s2.age+",s2的老师为 "+s2.teacher.name+", "+s2.teacher.age);
      
        }
    }
      
    
  • 运行结果:

    s1是kevin, 20,s1的老师为 zyn, 30
    s2是kevin, 20,s2的老师为 zyn, 30
    修改s1的name、age、teacher后
    s1是kevin11, 25,s1的老师为 zyn, 40
    s2是kevin, 20,s2的老师为 zyn, 40
    

2.2重写Object中的clone方法实现

  • 在Object类中有一个方法为clone()就是用来进行浅拷贝的,使用时需要注意:

    • 在Object类中,该方法是protected的,重写时要改为public;
    • 使用clone方法的类必须要实现Cloneable接口
  • 代码:

    class Student implements Cloneable{
        public int age;
        public String name;
        public Teacher teacher;
          
        public Student(int age, String name, Teacher teacher){
            this.age=age;
            this.name=name;
            this.teacher=teacher;
        }
        @Override
        public Object clone(){
            Object obj = null;
            try{
                obj = super.clone();
            }catch(CloneNotSupportedException e){
                e.printStackTrace();
            }
            return obj;
        }
          
    }
      
    class Teacher{
        public int age;
        public String name;
          
        public Teacher(int age, String name){
            this.age=age;
            this.name=name;
        }
    }
      
    public class ShallowCopy {
      
        public static void main(String[] args) throws CloneNotSupportedException {
            Teacher t1 = new Teacher(30,"zyn");
            Student s1 = new Student(20,"kevin",t1);
            Student s2 = (Student) s1.clone();
      
            System.out.println("s1是"+s1.name+", "+s1.age+",s1的老师为 "+s1.teacher.name+", "+s1.teacher.age);
            System.out.println("s2是"+s2.name+", "+s2.age+",s2的老师为 "+s2.teacher.name+", "+s2.teacher.age);
      
            s1.age=25;
            s1.name="kevin11";
            s1.teacher.age=40;
            System.out.println("修改s1的name、age、teacher后");
      
            System.out.println("s1是"+s1.name+", "+s1.age+",s1的老师为 "+s1.teacher.name+", "+s1.teacher.age);
            System.out.println("s2是"+s2.name+", "+s2.age+",s2的老师为 "+s2.teacher.name+", "+s2.teacher.age);
    	}
    }
      
    
  • 运行结果:

    s1是kevin, 20,s1的老师为 zyn, 30
    s2是kevin, 20,s2的老师为 zyn, 30
    修改s1的name、age、teacher后
    s1是kevin11, 25,s1的老师为 zyn, 40
    s2是kevin, 20,s2的老师为 zyn, 40
    

三、如何实现深拷贝

3.1重写Object中的clone方法实现

  • 基本思路和浅拷贝的思路差不多,主要是要对每个引用对象创建相应的对象

  • 代码:

    class Student implements Cloneable{
        public int age;
        public String name;
        public Teacher teacher;
          
        public Student(int age, String name, Teacher teacher){
            this.age=age;
            this.name=name;
            this.teacher=teacher;
        }
        @Override
        public Object clone(){
            Object obj = null;
            try{
                obj = super.clone();
            }catch(CloneNotSupportedException e){
                e.printStackTrace();
            }
            Student s = (Student)obj;
            s.teacher = (Teacher)this.teacher.clone();
            return obj;
        }
          
    }
      
    class Teacher implements Cloneable{
        public int age;
        public String name;
          
        public Teacher(int age, String name){
            this.age=age;
            this.name=name;
        }
          
        @Override
        public Object clone(){
            Object obj = null;
            try{
                obj = super.clone();
            }catch(CloneNotSupportedException e){
                e.printStackTrace();
            }
            return obj;
        }
    }
      
    public class DeepCopy {
      
        public static void main(String[] args) throws CloneNotSupportedException {
            Teacher t1 = new Teacher(30,"zyn");
            Student s1 = new Student(20,"kevin",t1);
            Student s2 = (Student) s1.clone();
      
            System.out.println("s1是"+s1.name+", "+s1.age+",s1的老师为 "+s1.teacher.name+", "+s1.teacher.age);
            System.out.println("s2是"+s2.name+", "+s2.age+",s2的老师为 "+s2.teacher.name+", "+s2.teacher.age);
      
            s1.age=25;
            s1.name="kevin11";
            s1.teacher.age=40;
            System.out.println("修改s1的name、age、teacher后");
      
            System.out.println("s1是"+s1.name+", "+s1.age+",s1的老师为 "+s1.teacher.name+", "+s1.teacher.age);
            System.out.println("s2是"+s2.name+", "+s2.age+",s2的老师为 "+s2.teacher.name+", "+s2.teacher.age);
      
        }
    }
    
  • 运行结果:

    s1是kevin, 20,s1的老师为 zyn, 30
    s2是kevin, 20,s2的老师为 zyn, 30
    修改s1的name、age、teacher后
    s1是kevin11, 25,s1的老师为 zyn, 40
    s2是kevin, 20,s2的老师为 zyn, 30
    

3.2通过序列化和反序列化来实现

  • 进行序列化时要将对象加入流中,该过程就是对象的深拷贝,原对象任在JVM运行时数据区域中,再通过反序列化从流中读取数据,可以获得新的拷贝对象

    • 进行序列化的类要实现Serializable接口
  • 代码:

    import java.io.*;
      
    public class DeepCopyBySerialization {
      
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            Teacher t1 = new Teacher(30,"zyn");
            Student s1 = new Student(20,"kevin",t1);
      
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(s1);
            oos.flush();
      
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
            Student s2 = (Student) ois.readObject();
      
      
            System.out.println("s1是"+s1.name+", "+s1.age+",s1的老师为 "+s1.teacher.name+", "+s1.teacher.age);
            System.out.println("s2是"+s2.name+", "+s2.age+",s2的老师为 "+s2.teacher.name+", "+s2.teacher.age);
      
            s1.age=25;
            s1.name="kevin11";
            s1.teacher.age=40;
            System.out.println("修改s1的name、age、teacher后");
      
            System.out.println("s1是"+s1.name+", "+s1.age+",s1的老师为 "+s1.teacher.name+", "+s1.teacher.age);
            System.out.println("s2是"+s2.name+", "+s2.age+",s2的老师为 "+s2.teacher.name+", "+s2.teacher.age);
      
      
      
        }
    }
      
      
    class Student implements Serializable {
        public int age;
        public String name;
        public Teacher teacher;
      
        public Student(int age, String name, Teacher teacher){
            this.age=age;
            this.name=name;
            this.teacher=teacher;
        }
      
    }
      
    class Teacher implements Serializable{
        public int age;
        public String name;
      
        public Teacher(int age, String name){
            this.age=age;
            this.name=name;
        }
      
    }
    
  • 运行结果:

    s1是kevin, 20,s1的老师为 zyn, 30
    s2是kevin, 20,s2的老师为 zyn, 30
    修改s1的name、age、teacher后
    s1是kevin11, 25,s1的老师为 zyn, 40
    s2是kevin, 20,s2的老师为 zyn, 30