Java深拷贝和浅拷贝

2020-02-27  本文已影响0人  程序员will

Java深拷贝和浅拷贝

对象拷贝有哪些

对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部数据。

Java中有三种类型的对象拷贝:

理解浅拷贝

什么是浅拷贝?
如何实现浅拷贝

下面来看一看实现浅拷贝的一个例子

public class Subject {
 
   private String name; 
   public Subject(String s) { 
      name = s; 
   } 

   public String getName() { 
      return name; 
   } 

   public void setName(String s) { 
      name = s; 
   } 
}

public class Student implements Cloneable { 
 
   // 对象引用 
   private Subject subj; 
   private String name; 
 
   public Student(String s, String sub) { 
      name = s; 
      subj = new Subject(sub); 
   } 
 
   public Subject getSubj() { 
      return subj; 
   } 
 
   public String getName() { 
      return name; 
   } 
 
   public void setName(String s) { 
      name = s; 
   } 
 
   /** 
    *  重写clone()方法 
    * @return 
    */ 
   public Object clone() { 
      //浅拷贝 
      try { 
         // 直接调用父类的clone()方法
         return super.clone(); 
      } catch (CloneNotSupportedException e) { 
         return null; 
      } 
   } 
}

public class CopyDemoMain {
    public static void main(String[] args) {
        // 原始对象
        Student stud = new Student("杨充", "潇湘剑雨");
        System.out.println("原始对象: " + stud.getName() + " - " + stud.getSubj().getName());

        // 拷贝对象
        Student clonedStud = (Student) stud.clone();
        System.out.println("拷贝对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());

        // 原始对象和拷贝对象是否一样:
        System.out.println("原始对象和拷贝对象是否一样: " + (stud == clonedStud));
        // 原始对象和拷贝对象的name属性是否一样
        System.out.println("原始对象和拷贝对象的name属性是否一样: " + (stud.getName() == clonedStud.getName()));
        // 原始对象和拷贝对象的subj属性是否一样
        System.out.println("原始对象和拷贝对象的subj属性是否一样: " + (stud.getSubj() == clonedStud.getSubj()));

        stud.setName("小杨逗比");
        stud.getSubj().setName("潇湘剑雨大侠");
        System.out.println("更新后的原始对象: " + stud.getName() + " - " + stud.getSubj().getName());
        System.out.println("更新原始对象后的克隆对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());
    }
}

输出结果如下:

Connected to the target VM, address: '127.0.0.1:57836', transport: 'socket'
原始对象: 杨充 - 潇湘剑雨
拷贝对象: 杨充 - 潇湘剑雨
原始对象和拷贝对象是否一样: false
原始对象和拷贝对象的name属性是否一样: true
原始对象和拷贝对象的subj属性是否一样: true
更新后的原始对象: 小杨逗比 - 潇湘剑雨大侠
更新原始对象后的克隆对象: 杨充 - 潇湘剑雨大侠
Disconnected from the target VM, address: '127.0.0.1:57836', transport: 'socket'

可以得出的结论

理解深拷贝

什么是深拷贝?
实现深拷贝案例

下面是实现深拷贝的一个例子。只是在浅拷贝的例子上做了一点小改动,Subject 和CopyTest 类都没有变化。

public class DeepStudent implements Cloneable {
    // 对象引用 
    private Subject subj;
    private String  name;

    public DeepStudent(String s, String sub) {
        name = s;
        subj = new Subject(sub);
    }

    public Subject getSubj() {
        return subj;
    }

    public String getName() {
        return name;
    }

    public void setName(String s) {
        name = s;
    }

    /**
     * 重写clone()方法
     *
     * @return
     */
    public Object clone() {
        // 深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立
        DeepStudent s = new DeepStudent(name, subj.getName());
        return s;
    }
}

输出结果如下:

Connected to the target VM, address: '127.0.0.1:60802', transport: 'socket'
原始对象: 杨充 - 潇湘剑雨
拷贝对象: 杨充 - 潇湘剑雨
原始对象和拷贝对象是否一样: false
原始对象和拷贝对象的name属性是否一样: true
原始对象和拷贝对象的subj属性是否一样: true
更新后的原始对象: 小杨逗比 - 潇湘剑雨大侠
更新原始对象后的克隆对象: 杨充 - 潇湘剑雨大侠
Disconnected from the target VM, address: '127.0.0.1:60802', transport: 'socket'

得出的结论

序列化进行拷贝

序列化属于深拷贝

可能你会问,序列化是属于那种类型拷贝?答案是:通过序列化来实现深拷贝。可以思考一下,为何序列化对象要用深拷贝而不是用浅拷贝呢?

注意要点

可以序列化是干什么的?它将整个对象图写入到一个持久化存储文件中并且当需要的时候把它读取回来, 这意味着当你需要把它读取回来时你需要整个对象图的一个拷贝。这就是当你深拷贝一个对象时真正需要的东西。请注意,当你通过序列化进行深拷贝时,必须确保对象图中所有类都是可序列化的。

序列化案例

看一下下面案例,很简单,只需要实现Serializable这个接口。Android中还可以实现Parcelable接口。

public class ColoredCircle implements Serializable { 
 
   private int x; 
   private int y; 
 
   public ColoredCircle(int x, int y) { 
      this.x = x; 
      this.y = y; 
   } 
 
   public int getX() { 
      return x; 
   } 
 
   public void setX(int x) { 
      this.x = x; 
   } 
 
   public int getY() { 
      return y; 
   } 
 
   public void setY(int y) { 
      this.y = y; 
   } 
 
   @Override 
   public String toString() { 
      return "x=" + x + ", y=" + y; 
   } 
}

public class DouBi implements Serializable {

    private static final long serialVersionUID = -8752043194511106066L;

    private int x;
    private int y;

    public DouBi(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

public class CopyDemoMain3 {
    public static void main(String[] args) {
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            // 创建原始的可序列化对象
            DouBi c1 = new DouBi(100, 100);
            System.out.println("原始的对象 = " + c1);
            DouBi c2 = null;
            // 通过序列化实现深拷贝
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            // 序列化以及传递这个对象
            oos.writeObject(c1);
            oos.flush();
            ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bin);
            // 返回新的对象
            c2 = (DouBi) ois.readObject();
            // 校验内容是否相同
            System.out.println("复制后的对象   = " + c2);
            // 改变原始对象的内容
            c1.setX(200);
            c1.setY(200);
            // 查看每一个现在的内容
            System.out.println("查看原始的对象 = " + c1);
            System.out.println("查看复制的对象 = " + c2);
        } catch (IOException e) {
            System.out.println("Exception in main = " + e);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出结果如下:

Connected to the target VM, address: '127.0.0.1:62095', transport: 'socket'
原始的对象 = com.vernon.test.copy.DouBi@1134affc
复制后的对象   = com.vernon.test.copy.DouBi@1b604f19
查看原始的对象 = com.vernon.test.copy.DouBi@1134affc
查看复制的对象 = com.vernon.test.copy.DouBi@1b604f19
Disconnected from the target VM, address: '127.0.0.1:62095', transport: 'socket'

注意:需要做以下几件事儿:

延迟拷贝

如何选择拷贝方式

数组的拷贝

基本数据类型数组

如下所示

public void test4() {
    int[] lNumbers1 = new int[5];
    int[] rNumbers1 = Arrays.copyOf(lNumbers1, lNumbers1.length);
    rNumbers1[0] = 1;
    boolean first = lNumbers1[0] == rNumbers1[0];
    Log.d("小杨逗比", "lNumbers2[0]=" + lNumbers1[0] + ",rNumbers2[0]=" + rNumbers1[0]+"---"+first);

    int[] lNumbers3 = new int[5];
    int[] rNumbers3 = lNumbers3.clone();
    rNumbers3[0] = 1;
    boolean second = lNumbers3[0] == rNumbers3[0];
    Log.d("小杨逗比", "lNumbers3[0]=" + lNumbers3[0] + ",rNumbers3[0]=" + rNumbers3[0]+"---"+second);
}

打印结果如下所示

2019-03-25 14:28:09.907 30316-30316/org.yczbj.ycrefreshview D/小杨逗比: lNumbers2[0]=0,rNumbers2[0]=1---false
2019-03-25 14:28:09.907 30316-30316/org.yczbj.ycrefreshview D/小杨逗比: lNumbers3[0]=0,rNumbers3[0]=1---false

引用数据类型数组

如下所示

public static void test5() {
    People[] lNumbers1 = new People[5];
    lNumbers1[0] = new People();
    People[] rNumbers1 = lNumbers1;
    boolean first = lNumbers1[0].equals(rNumbers1[0]);
    Log.d("小杨逗比", "lNumbers1[0]=" + lNumbers1[0] + ",rNumbers1[0]=" + rNumbers1[0]+"--"+first);

    People[] lNumbers2 = new People[5];
    lNumbers2[0] = new People();
    People[] rNumbers2 = Arrays.copyOf(lNumbers2, lNumbers2.length);
    boolean second = lNumbers2[0].equals(rNumbers2[0]);
    Log.d("小杨逗比", "lNumbers2[0]=" + lNumbers2[0] + ",rNumbers2[0]=" + rNumbers2[0]+"--"+second);

    People[] lNumbers3 = new People[5];
    lNumbers3[0] = new People();
    People[] rNumbers3 = lNumbers3.clone();
    boolean third = lNumbers3[0].equals(rNumbers3[0]);
    Log.d("小杨逗比", "lNumbers3[0]=" + lNumbers3[0] + ",rNumbers3[0]=" + rNumbers3[0]+"--"+third);
}

public static class People implements Cloneable {

    int age;
    Holder holder;

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

    public static class Holder {
        int holderValue;
    }
}

打印日志如下

2019-03-25 14:53:17.054 31093-31093/org.yczbj.ycrefreshview D/小杨逗比: lNumbers1[0]=org.yczbj.ycrefreshview.MainActivity$People@46a2c18,rNumbers1[0]=org.yczbj.ycrefreshview.MainActivity$People@46a2c18--true
2019-03-25 14:53:17.054 31093-31093/org.yczbj.ycrefreshview D/小杨逗比: lNumbers2[0]=org.yczbj.ycrefreshview.MainActivity$People@d344671,rNumbers2[0]=org.yczbj.ycrefreshview.MainActivity$People@d344671--true
2019-03-25 14:53:17.054 31093-31093/org.yczbj.ycrefreshview D/小杨逗比: lNumbers3[0]=org.yczbj.ycrefreshview.MainActivity$People@91e9c56,rNumbers3[0]=org.yczbj.ycrefreshview.MainActivity$People@91e9c56--true

集合的拷贝

集合浅拷贝

构造函数和 clone() 默认都是浅拷贝

public static void test6() {
    ArrayList<People> lPeoples = new ArrayList<>();
    People people1 = new People();
    lPeoples.add(people1);
    Log.d("小杨逗比", "lPeoples[0]=" + lPeoples.get(0));
    ArrayList<People> rPeoples = (ArrayList<People>) lPeoples.clone();
    Log.d("小杨逗比", "rPeoples[0]=" + rPeoples.get(0));
    boolean b = lPeoples.get(0).equals(rPeoples.get(0));
    Log.d("小杨逗比", "比较两个对象" + b);
}

public static class People implements Cloneable {

    int age;
    Holder holder;

    @Override
    protected Object clone() {
        try {
            People people = (People) super.clone();
            people.holder = (People.Holder) this.holder.clone();
            return people;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static class Holder implements Cloneable {

        int holderValue;

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

打印日志

2019-03-25 14:56:56.931 31454-31454/org.yczbj.ycrefreshview D/小杨逗比: lPeoples[0]=org.yczbj.ycrefreshview.MainActivity$People@46a2c18
2019-03-25 14:56:56.931 31454-31454/org.yczbj.ycrefreshview D/小杨逗比: rPeoples[0]=org.yczbj.ycrefreshview.MainActivity$People@46a2c18
2019-03-25 14:56:56.931 31454-31454/org.yczbj.ycrefreshview D/小杨逗比: 比较两个对象true

集合深拷贝

在某些特殊情况下,如果需要实现集合的深拷贝,那就要创建一个新的集合,然后通过深拷贝原先集合中的每个元素,将这些元素加入到新的集合当中。

public static void test7() {
    ArrayList<People> lPeoples = new ArrayList<>();
    People people1 = new People();
    people1.holder = new People.Holder();
    lPeoples.add(people1);
    Log.d("小杨逗比", "lPeoples[0]=" + lPeoples.get(0));
    ArrayList<People> rPeoples = new ArrayList<>();
    for (People people : lPeoples) {
        rPeoples.add((People) people.clone());
    }
    Log.d("小杨逗比", "rPeoples[0]=" + rPeoples.get(0));
    boolean b = lPeoples.get(0).equals(rPeoples.get(0));
    Log.d("小杨逗比", "比较两个对象" + b);
}

public static class People implements Cloneable {

    int age;
    Holder holder;

    @Override
    protected Object clone() {
        try {
            People people = (People) super.clone();
            people.holder = (People.Holder) this.holder.clone();
            return people;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static class Holder implements Cloneable {

        int holderValue;

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

打印日志

2019-03-25 15:00:54.610 31670-31670/org.yczbj.ycrefreshview D/小杨逗比: lPeoples[0]=org.yczbj.ycrefreshview.MainActivity$People@46a2c18
2019-03-25 15:00:54.610 31670-31670/org.yczbj.ycrefreshview D/小杨逗比: rPeoples[0]=org.yczbj.ycrefreshview.MainActivity$People@d344671
2019-03-25 15:00:54.610 31670-31670/org.yczbj.ycrefreshview D/小杨逗比: 比较两个对象false

上一篇 下一篇

猜你喜欢

热点阅读