精华内容
下载资源
问答
  • 浅拷贝被复制的对象的所有的变量都与原对象有相同的值,而所有的引用对象仍然指向原来的对象。换言之,浅拷贝 不复制引用对象。1 class Experience {2 private String skill;3 public void setSkill(String skill){4...

    浅拷贝

    被复制的对象的所有的变量都与原对象有相同的值,而所有的引用对象仍然指向原来的对象。换言之,浅拷贝 不复制引用对象。

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1 class Experience {

    2 private String skill;

    3 public void setSkill(String skill){

    4 this.skill = skill;

    5 }

    6 public void setExperience(String skill){

    7 this.skill = skill;

    8 }

    9

    10 @Override

    11 public String toString() {

    12 return skill;

    13 }

    14 }

    15

    16 public class CloneTest implements Cloneable{

    17

    18 private int age ;

    19 private Experience experience;

    20

    21 public CloneTest(){

    22 this.age = 10;

    23 this.experience = new Experience();

    24 }

    25 public Experience getExperience() {

    26 return experience;

    27 }

    28 public void setExperience(String skill){

    29 experience.setExperience(skill);

    30 }

    31 public void show(){

    32 System.out.println(experience.toString());

    33 }

    34 public int getAge() {

    35 return age;

    36 }

    37 @Override

    38 protected Object clone() throws CloneNotSupportedException {

    39 return (CloneTest) super.clone();40 }

    41 }

    42 //测试类

    43 class MianTest{

    44

    45 public static void main(String[] args) throws CloneNotSupportedException {

    46 CloneTest test = new CloneTest();

    47 test.setExperience("我是小明,我精通Java,C++的复制粘贴");

    48 test.show();

    49 CloneTest cloneTest = (CloneTest) test.clone();

    50 cloneTest.show();

    51 cloneTest.setExperience("我是小明的副本,我精通Java,C++");

    52 cloneTest.show();

    53 test.show();54 System.out.println(cloneTest.getAge());

    55 }

    56 }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    输出的结果:

    我是小明,我精通Java,C++的复制粘贴

    我是小明,我精通Java,C++的复制粘贴

    我是小明的副本,我精通Java,C++

    我是小明的副本,我精通Java,C++

    10

    从结果中不难看出,拷贝的副本改变了Experience的skill属性,原类中的skill属性打印出来也是修改后的结果,说明引用 类型的拷贝没有将对象拷贝,引用的指向还是原类中的指向

    深拷贝

    除了被复制的对象的所有变量都有原来对象的值之外,还把引用对象也指向了被复制的新对象

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1 class Experience implements Cloneable{

    2 private String skill;

    3 public void setSkill(String skill){

    4 this.skill = skill;

    5 }

    6 public void setExperience(String skill){

    7 this.skill = skill;

    8 }

    9

    10 @Override

    11 public String toString() {

    12 return skill;

    13 }

    14

    15 @Override

    16 protected Object clone() throws CloneNotSupportedException {

    17 return super.clone();

    18 }

    19 }

    20

    21 public class CloneTest implements Cloneable{

    22

    23 private int age ;

    24 private Experience experience;

    25

    26 public CloneTest(){

    27 this.age = 10;

    28 this.experience = new Experience();

    29 }

    30 public Experience getExperience() {

    31 return experience;

    32 }

    33 public void setExperience(String skill){

    34 experience.setExperience(skill);

    35 }

    36 public void show(){

    37 System.out.println(experience.toString());

    38 }

    39 public int getAge() {

    40 return age;

    41 }

    42 @Override

    43 protected Object clone() throws CloneNotSupportedException {

    44 CloneTest o = (CloneTest) super.clone();

    45 o.experience = (Experience) o.getExperience().clone();46 return o;

    47 }

    48 }

    49 class MianTest{

    50

    51 public static void main(String[] args) throws CloneNotSupportedException {

    52 CloneTest test = new CloneTest();

    53 test.setExperience("我是小明,我精通Java,C++的复制粘贴");

    54 test.show();

    55 CloneTest cloneTest = (CloneTest) test.clone();

    56 cloneTest.show();

    57 cloneTest.setExperience("我是小明的副本,我精通Java,C++");

    58 cloneTest.show();

    59 test.show();60 System.out.println(cloneTest.getAge());

    61 }

    62 }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    输出的结果:

    我是小明,我精通Java,C++的复制粘贴

    我是小明,我精通Java,C++的复制粘贴

    我是小明的副本,我精通Java,C++

    我是小明,我精通Java,C++的复制粘贴

    10

    可以看出和第一次的结果不同了。

    o.experience =(Experience) o.getExperience().clone();

    加了这句之后效果不同的。说明实现了深拷贝,原引用对象也复制过来了

    展开全文
  • Java深拷贝和浅拷贝

    2021-01-27 01:49:19
    目录介绍01.对象拷贝有哪些02.理解浅拷贝2.1 什么是浅拷贝2.2 实现浅拷贝案例03.理解深拷贝3.1 什么是深拷贝3.2 实现深拷贝案例04....集合的拷贝8.1 集合浅拷贝8.2 集合深拷贝好消息博客笔记大汇总【16...

    目录介绍

    01.对象拷贝有哪些

    02.理解浅拷贝

    2.1 什么是浅拷贝

    2.2 实现浅拷贝案例

    03.理解深拷贝

    3.1 什么是深拷贝

    3.2 实现深拷贝案例

    04.序列化进行拷贝

    4.1 序列化属于深拷贝

    4.2 注意要点

    4.3 序列化案例

    05.延迟拷贝

    06.如何选择拷贝方式

    07.数组的拷贝

    7.1 基本数据类型数组

    7.2 引用数据类型数组

    08.集合的拷贝

    8.1 集合浅拷贝

    8.2 集合深拷贝

    好消息

    博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!

    如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

    01.对象拷贝有哪些

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

    Java中有三种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)、延迟拷贝(Lazy Copy)。

    02.理解浅拷贝

    2.1 什么是浅拷贝

    浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。

    如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

    8f75d917ac68ffa5bb6870388ead09a3.png

    在上图中,SourceObject有一个int类型的属性 "field1"和一个引用类型属性"refObj"(引用ContainedObject类型的对象)。当对SourceObject做浅拷贝时,创建了CopiedObject,它有一个包含"field1"拷贝值的属性"field2"以及仍指向refObj本身的引用。由于"field1"是基本类型,所以只是将它的值拷贝给"field2",但是由于"refObj"是一个引用类型, 所以CopiedObject指向"refObj"相同的地址。因此对SourceObject中的"refObj"所做的任何改变都会影响到CopiedObject。

    2.2 如何实现浅拷贝

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

    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()方法

    */

    public Object clone() {

    //浅拷贝

    try {

    // 直接调用父类的clone()方法

    return super.clone();

    } catch (CloneNotSupportedException e) {

    return null;

    }

    }

    }

    ```

    ```

    private void test1(){

    // 原始对象

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

    }

    ```

    输出结果如下:

    2019-03-23 13:50:57.518 24704-24704/com.ycbjie.other I/System.out: 原始对象: 杨充 - 潇湘剑雨

    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 拷贝对象: 杨充 - 潇湘剑雨

    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象是否一样: false

    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象的name属性是否一样: true

    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象的subj属性是否一样: true

    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 更新后的原始对象: 小杨逗比 - 潇湘剑雨大侠

    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 更新原始对象后的克隆对象: 杨充 - 潇湘剑雨大侠

    可以得出的结论

    在这个例子中,让要拷贝的类Student实现了Clonable接口并重写Object类的clone()方法,然后在方法内部调用super.clone()方法。从输出结果中我们可以看到,对原始对象stud的"name"属性所做的改变并没有影响到拷贝对象clonedStud,但是对引用对象subj的"name"属性所做的改变影响到了拷贝对象clonedStud。

    03.理解深拷贝

    3.1 什么是深拷贝

    深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

    4ac782758672a447e9c73ac3c70b4aa0.png

    在上图中,SourceObject有一个int类型的属性 "field1"和一个引用类型属性"refObj1"(引用ContainedObject类型的对象)。当对SourceObject做深拷贝时,创建了CopiedObject,它有一个包含"field1"拷贝值的属性"field2"以及包含"refObj1"拷贝值的引用类型属性"refObj2" 。因此对SourceObject中的"refObj"所做的任何改变都不会影响到CopiedObject

    3.2 实现深拷贝案例

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

    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()方法

    *

    */

    public Object clone() {

    // 深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立

    Student s = new Student(name, subj.getName());

    return s;

    }

    }

    ```

    输出结果如下:

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 原始对象: 杨充 - 潇湘剑雨

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 拷贝对象: 杨充 - 潇湘剑雨

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 原始对象和拷贝对象是否一样: false

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 原始对象和拷贝对象的name属性是否一样: true

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 原始对象和拷贝对象的subj属性是否一样: false

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 更新后的原始对象: 小杨逗比 - 潇湘剑雨大侠

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 更新原始对象后的克隆对象: 杨充 - 潇湘剑雨

    得出的结论

    很容易发现clone()方法中的一点变化。因为它是深拷贝,所以你需要创建拷贝类的一个对象。因为在Student类中有对象引用,所以需要在Student类中实现Cloneable接口并且重写clone方法。

    04.序列化进行拷贝

    4.1 序列化属于深拷贝

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

    4.2 注意要点

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

    4.3 序列化案例

    看一下下面案例,很简单,只需要实现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;

    }

    }

    private void test3() {

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

    }

    }

    }

    }

    输出结果如下:

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 原始的对象 = x=100, y=100

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 复制后的对象 = x=100, y=100

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 查看原始的对象 = x=200, y=200

    2019-03-23 13:53:48.096 25123-25123/com.ycbjie.other I/System.out: 查看复制的对象 = x=100, y=100

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

    确保对象图中的所有类都是可序列化的

    创建输入输出流

    使用这个输入输出流来创建对象输入和对象输出流

    将你想要拷贝的对象传递给对象输出流

    从对象输入流中读取新的对象并且转换回你所发送的对象的类

    得出的结论

    在这个例子中,创建了一个DouBi对象c1然后将它序列化 (将它写到ByteArrayOutputStream中). 然后我反序列化这个序列化后的对象并将它保存到c2中。随后我修改了原始对象c1。然后结果如你所见,c1不同于c2,对c1所做的任何修改都不会影响c2。

    注意,序列化这种方式有其自身的限制和问题:因为无法序列化transient变量, 使用这种方法将无法拷贝transient变量。再就是性能问题。创建一个socket, 序列化一个对象, 通过socket传输它, 然后反序列化它,这个过程与调用已有对象的方法相比是很慢的。所以在性能上会有天壤之别。如果性能对你的代码来说是至关重要的,建议不要使用这种方式。它比通过实现Clonable接口这种方式来进行深拷贝几乎多花100倍的时间。

    05.延迟拷贝

    延迟拷贝是浅拷贝和深拷贝的一个组合,实际上很少会使用。这个以前几乎都没听说过,后来看书才知道有这么一种拷贝!

    当最开始拷贝一个对象时,会使用速度较快的浅拷贝,还会使用一个计数器来记录有多少对象共享这个数据。当程序想要修改原始的对象时,它会决定数据是否被共享(通过检查计数器)并根据需要进行深拷贝。

    延迟拷贝从外面看起来就是深拷贝,但是只要有可能它就会利用浅拷贝的速度。当原始对象中的引用不经常改变的时候可以使用延迟拷贝。由于存在计数器,效率下降很高,但只是常量级的开销。而且, 在某些情况下, 循环引用会导致一些问题。

    06.如何选择拷贝方式

    如果对象的属性全是基本类型的,那么可以使用浅拷贝。

    如果对象有引用属性,那就要基于具体的需求来选择浅拷贝还是深拷贝。

    意思是如果对象引用任何时候都不会被改变,那么没必要使用深拷贝,只需要使用浅拷贝就行了。如果对象引用经常改变,那么就要使用深拷贝。没有一成不变的规则,一切都取决于具体需求。

    07.数组的拷贝

    数组除了默认实现了clone()方法之外,还提供了Arrays.copyOf方法用于拷贝,这两者都是浅拷贝。

    7.1 基本数据类型数组

    如下所示

    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

    7.2 引用数据类型数组

    如下所示

    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

    08.集合的拷贝

    集合的拷贝也是我们平时经常会遇到的,一般情况下,我们都是用浅拷贝来实现,即通过构造函数或者clone方法。

    8.1 集合浅拷贝

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

    public static void test6() {

    ArrayList lPeoples = new ArrayList<>();

    People people1 = new People();

    lPeoples.add(people1);

    Log.d("小杨逗比", "lPeoples[0]=" + lPeoples.get(0));

    ArrayList rPeoples = (ArrayList) 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

    8.2 集合深拷贝

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

    public static void test7() {

    ArrayList lPeoples = new ArrayList<>();

    People people1 = new People();

    people1.holder = new People.Holder();

    lPeoples.add(people1);

    Log.d("小杨逗比", "lPeoples[0]=" + lPeoples.get(0));

    ArrayList 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

    其他介绍

    01.关于博客汇总链接

    02.关于我的博客

    展开全文
  • 总结:深拷贝和浅拷贝区别浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象消耗小。深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。深拷贝把要复制的对象所引用的对象都复制了一遍。一、拷贝的引入(1)...

    总结:深拷贝和浅拷贝区别

    浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象消耗小。

    深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。深拷贝把要复制的对象所引用的对象都复制了一遍。

    一、拷贝的引入

    (1)、引用拷贝

    创建一个指向对象的引用变量的拷贝。

    Teacher teacher = new Teacher("Taylor",26);

    Teacher otherteacher = teacher;

    System.out.println(teacher);

    System.out.println(otherteacher);

    1

    2

    3

    4

    输出结果:

    blog.Teacher@355da254

    blog.Teacher@355da254

    1

    2

    结果分析:由输出结果可以看出,它们的地址值是相同的,那么它们肯定是同一个对象。teacher和otherteacher的只是引用而已,他们都指向了一个相同的对象Teacher(“Taylor”,26)。 这就叫做引用拷贝。

    8be15323fd67911b4fa7dd423f815372.png

    (2)、对象拷贝

    创建对象本身的一个副本。

    Teacher teacher = new Teacher("Swift",26);

    Teacher otherteacher = (Teacher)teacher.clone();

    System.out.println(teacher);

    System.out.println(otherteacher);

    1

    2

    3

    4

    输出结果:

    blog.Teacher@355da254

    blog.Teacher@4dc63996

    1

    2

    结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。

    5b1b3a1c4f7efe6916df4aac274ff95d.png

    注:深拷贝和浅拷贝都是对象拷贝

    二、浅拷贝

    (1)、定义

    被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。

    简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象

    (2)、浅拷贝实例

    package com.test;

    public class ShallowCopy {

    public static void main(String[] args) throws CloneNotSupportedException {

    Teacher teacher = new Teacher();

    teacher.setName("riemann");

    teacher.setAge(27);

    Student2 student1 = new Student2();

    student1.setName("edgar");

    student1.setAge(18);

    student1.setTeacher(teacher);

    Student2 student2 = (Student2) student1.clone();

    System.out.println("拷贝后");

    System.out.println(student2.getName());

    System.out.println(student2.getAge());

    System.out.println(student2.getTeacher().getName());

    System.out.println(student2.getTeacher().getAge());

    System.out.println("修改老师的信息后-------------");

    // 修改老师的信息

    teacher.setName("Games");

    System.out.println(student1.getTeacher().getName());

    System.out.println(student2.getTeacher().getName());

    }

    }

    class Teacher implements Cloneable {

    private String name;

    private int age;

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public int getAge() {

    return age;

    }

    public void setAge(int age) {

    this.age = age;

    }

    }

    class Student2 implements Cloneable {

    private String name;

    private int age;

    private Teacher teacher;

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public int getAge() {

    return age;

    }

    public void setAge(int age) {

    this.age = age;

    }

    public Teacher getTeacher() {

    return teacher;

    }

    public void setTeacher(Teacher teacher) {

    this.teacher = teacher;

    }

    public Object clone() throws CloneNotSupportedException {

    Object object = super.clone();

    return object;

    }

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    输出结果:

    拷贝后

    edgar

    18

    riemann

    27

    修改老师的信息后-------------

    Games

    Games

    1

    2

    3

    4

    5

    6

    7

    8

    结果分析: 两个引用student1和student2指向不同的两个对象,但是两个引用student1和student2中的两个teacher引用指向的是同一个对象,所以说明是浅拷贝。

    9da7b0de42d56e17b36e71ee28bce5e5.png

    三、深拷贝

    (1)、定义

    深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

    简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

    (2)、深拷贝实例

    package com.test;

    public class DeepCopy {

    public static void main(String[] args) throws CloneNotSupportedException {

    Teacher2 teacher = new Teacher2();

    teacher.setName("riemann");

    teacher.setAge(27);

    Student3 student1 = new Student3();

    student1.setName("edgar");

    student1.setAge(18);

    student1.setTeacher(teacher);

    Student3 student2 = (Student3) student1.clone();

    System.out.println("拷贝后");

    System.out.println(student2.getName());

    System.out.println(student2.getAge());

    System.out.println(student2.getTeacher().getName());

    System.out.println(student2.getTeacher().getAge());

    System.out.println("修改老师的信息后-------------");

    // 修改老师的信息

    teacher.setName("Games");

    System.out.println(student1.getTeacher().getName());

    System.out.println(student2.getTeacher().getName());

    }

    }

    class Teacher2 implements Cloneable {

    private String name;

    private int age;

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public int getAge() {

    return age;

    }

    public void setAge(int age) {

    this.age = age;

    }

    public Object clone() throws CloneNotSupportedException {

    return super.clone();

    }

    }

    class Student3 implements Cloneable {

    private String name;

    private int age;

    private Teacher2 teacher;

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public int getAge() {

    return age;

    }

    public void setAge(int age) {

    this.age = age;

    }

    public Teacher2 getTeacher() {

    return teacher;

    }

    public void setTeacher(Teacher2 teacher) {

    this.teacher = teacher;

    }

    public Object clone() throws CloneNotSupportedException {

    // 浅复制时:

    // Object object = super.clone();

    // return object;

    // 改为深复制:

    Student3 student = (Student3) super.clone();

    // 本来是浅复制,现在将Teacher对象复制一份并重新set进来

    student.setTeacher((Teacher2) student.getTeacher().clone());

    return student;

    }

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    92

    93

    94

    95

    输出结果:

    拷贝后

    edgar

    18

    riemann

    27

    修改老师的信息后-------------

    Games

    riemann

    1

    2

    3

    4

    5

    6

    7

    8

    结果分析:两个引用student1和student2指向不同的两个对象,两个引用student1和student2中的两个teacher引用指向的是两个对象,但对teacher对象的修改只能影响student1对象,所以说是深拷贝。

    041167c75c375e97c5f79e3cd44652f4.png

    展开全文
  • 一、前言任何变成语言中,其实都有浅拷贝和深拷贝的概念,Java 中也不例外。在对一个现有的对象进行拷贝操作的时候,是有浅拷贝和深拷贝之分的,他们在实际使用中,区别很大,如果对其进行混淆,可能会引发一些难以...

    一、前言

    任何变成语言中,其实都有浅拷贝和深拷贝的概念,Java 中也不例外。在对一个现有的对象进行拷贝操作的时候,是有浅拷贝和深拷贝之分的,他们在实际使用中,区别很大,如果对其进行混淆,可能会引发一些难以排查的问题。

    本文就在 Java 中的深拷贝和浅拷贝做一个详细的解说。

    二、什么是浅拷贝和深拷贝

    首先需要明白,浅拷贝和深拷贝都是针对一个已有对象的操作。那先来看看浅拷贝和深拷贝的概念。

    在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 『 = 』号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。

    而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

    所以到现在,就应该了解了,所谓的浅拷贝和深拷贝,只是在拷贝对象的时候,对 类的实例对象 这种引用数据类型的不同操作而已。

    总结来说:

    1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。

    cd475719994e5144d036ee6d683370d5.png

    2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

    25cebb48cb1b9a075258051aff0c479d.png

    三、Java 中的 clone()

    3.1 Object 上的 clone() 方法

    在 Java 中,所有的 Class 都继承自 Object ,而在 Object 上,存在一个 clone() 方法,它被声明为了 protected ,所以我们可以在其子类中,使用它。

    而无论是浅拷贝还是深拷贝,都需要实现 clone() 方法,来完成操作。

    9d601f33cfd92d8abcfcdba60dc7b61b.png

    可以看到,它的实现非常的简单,它限制所有调用 clone() 方法的对象,都必须实现 Cloneable 接口,否者将抛出 CloneNotSupportedException 这个异常。最终会调用 internalClone() 方法来完成具体的操作。而 internalClone() 方法,实则是一个 native 的方法。对此我们就没必要深究了,只需要知道它可以 clone() 一个对象得到一个新的对象实例即可。

    ecf5743fd41ca62b46385152f22a3829.png

    而反观 Cloneable 接口,可以看到它其实什么方法都不需要实现。对他可以简单的理解只是一个标记,是开发者允许这个对象被拷贝。

    3.2 浅拷贝

    先来看看浅拷贝的例子。

    首先创建一个 class 为 FatherClass ,对其实现 Cloneable 接口,并且重写 clone() 方法。

    705df1cb0a5137d6abd418f030885369.png

    然后先正常 new 一个 FatherClass 对象,再使用 clone() 方法创建一个新的对象。

    ca602b1a3e98c78a396a54becbb24c8c.png

    最后看看输出的 Log :

    I/cxmyDev: fatherA == fatherB : false

    I/cxmyDev: fatherA hash : 560973324

    I/cxmyDev: fatherB hash : 560938740

    I/cxmyDev: fatherA name : 张三

    I/cxmyDev: fatherB name : 张三

    可以看到,使用 clone() 方法,从 == 和 hashCode 的不同可以看出,clone() 方法实则是真的创建了一个新的对象。

    但这只是一次浅拷贝的操作。

    来验证这一点,继续看下去,在 FatherClass 中,还有一个 ChildClass 的对象 child ,clone() 方法是否也可以正常复制它呢?改写一个上面的 Demo。

    4acea3a7fce5e643f09a8c2ef18108c1.png

    看到,这里将其内的 child 进行负责,用起来看看输出的 Log 效果。

    I/cxmyDev: fatherA == fatherB : false

    I/cxmyDev: fatherA hash : 560975188

    I/cxmyDev: fatherB hash : 560872384

    I/cxmyDev: fatherA name : 张三

    I/cxmyDev: fatherB name : 张三

    I/cxmyDev: ==================

    I/cxmyDev: A.child == B.child : true

    I/cxmyDev: fatherA.child hash : 560891436

    I/cxmyDev: fatherB.child hash : 560891436

    从最后对 child 的输出可以看到,A 和 B 的 child 对象,实际上还是指向了统一个对象,只对对它的引用进行了传递。

    3.3 深拷贝

    既然已经了解了对 clone() 方法,只能对当前对象进行浅拷贝,引用类型依然是在传递引用。

    那么,如何进行一个深拷贝呢?

    比较常用的方案有两种:

    序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。

    继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()。

    继续改写上面的 Demo ,让 ChildClass 也实现 Cloneable 接口。

    52ba8b2fee71a2b9c34287538d48c515.png

    最重要的代码就在 FatherClass.clone() 中,它对其内的 child ,再进行了一次 clone() 操作。

    再来看看输出的 Log。

    I/cxmyDev: fatherA == fatherB : false

    I/cxmyDev: fatherA hash : 561056732

    I/cxmyDev: fatherB hash : 561057344

    I/cxmyDev: fatherA name : 张三

    I/cxmyDev: fatherB name : 张三

    I/cxmyDev: ==================

    I/cxmyDev: A.child == B.child : false

    I/cxmyDev: fatherA.child hash : 561057304

    I/cxmyDev: fatherB.child hash : 561057360

    可以看到,对 child 也进行了一次拷贝,这实则是对 ChildClass 进行的浅拷贝,但是对于 FatherClass 而言,则是一次深拷贝。

    其实深拷贝的思路都差不多,序列化也好,使用 clone() 也好,实际上都是需要我们自己来编写拷贝的规则,最终实现深拷贝的目的。

    如果想要实现深拷贝,推荐使用 clone() 方法,这样只需要每个类自己维护自己即可,而无需关心内部其他的对象中,其他的参数是否也需要 clone() 。

    四、总结

    到现在基本上就已经梳理清楚,Java 中浅拷贝和深拷贝的概念了。

    实则浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。

    展开全文
  • java深拷贝和浅拷贝

    2021-02-12 09:44:22
    转自http://blog.sina.com.cn/s/blog_700aa8830101jtlf.htmlJava中对象的创建clone顾名思义就是复制, 在Java语言中, clone...那么在java语言中,有几种方式可以创建对象呢?1 使用new操作符创建一个对象2 使用clon...
  • Java拷贝和浅拷贝深拷贝(deep clone)与浅拷贝(shallow clone)浅拷贝(浅复制、浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅拷贝仅仅复制所...
  • 展开全部浅拷贝:只复制一个对象,对象内部存在的指向其他对象数组或者62616964757a686964616fe78988e69d8331333335336534引用则不复制深拷贝:对象,对象内部的引用均复制示例:publicstaticObjectcopy...
  • JAVA深拷贝和浅拷贝

    2021-01-28 16:55:25
    而拷贝又分为深拷贝和浅拷贝。很幸运,通过本文你可以了解到它们的区别。 首先,我们先来看这样一段代码: @Data public class Person implements Cloneable { private String name; private Person father; ...
  • 浅拷贝与深拷贝浅拷贝就是指两个对象共同拥有同一个值,一个对象改变了该值,也会影响到另一个对象。深拷贝就是两个对象的值相等,但是互相独立。Java中常用的拷贝操作有三个,operator =、拷贝构造函数 clone()...
  • Java 深拷贝和浅拷贝

    2021-02-22 23:26:46
    Java 深拷贝和浅拷贝 值类型 vs 引用类型 引用类型,操作时一般来说采用的是引用传递的方式; 基本类型,操作时一般采取的则是值传递的方式 为了说明,下面有两个类:Student和Subject,分别表示「学生」以及「学习...
  • 一、java拷贝分类 (1)、引用拷贝 创建一个指向对象的引用变量的拷贝。 Teacher teacher = new Teacher("Taylor",26); Teacher otherteacher = teacher; System.out.println(teacher); System....
  • Java深拷贝和浅拷贝

    2021-01-17 14:08:46
    关于Java深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象。可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用传递将会...
  • java实现拷贝最直观的做法用object类中的clone()方法,而想要使用...1.浅拷贝public class FatherClass implements Cloneable{private String name;private String age;private ChildA childA;private ChildB childB...
  • Java拷贝之深拷贝和浅拷贝如何使用clone方法浅拷贝深拷贝实现方式 首先,在学习拷贝之前,我们先要了解一些东西。当我们使用clone方法进行拷贝时,对于基本类型,我们都是拷贝其值。而对于引用类型,我们拷贝的是其...
  • 首先你需要了解基本数据类型引用数据类型,值传递引用传递,这里就不展开赘述了,不了解的可以自行递归。2.以下面公共代码为例的结构图是这样的image.png敲黑板!!!! 一定要学好基础知识,否则当你打开一篇...
  • 克隆和深克隆 1、克隆 在克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象克隆对象的成员变量...
  • Java语言中,当我们需要拷贝一个Java对象的时候,常见的会有两种方式的拷贝:浅拷贝和深拷贝。 一、浅拷贝 浅拷贝只是拷贝了源对象的地址,所以源对象的任何值发生变化时,拷贝对象的值也会随之发生变化。 浅拷贝...
  • java 浅拷贝和深拷贝

    2021-01-17 14:08:45
    实现拷贝有几点:1)实现Cloneable接口2)重写Object类中的clone方法,并将可见性从protect改为public3)克隆需要调用super.clone(),也就是Object的实现方法浅拷贝和深拷贝的区别:浅拷贝是指拷贝对象时仅仅拷贝对象...
  • 之前谈到了关于clone()方法一些知识点,继续深入去了解clone中深拷贝与浅拷贝的区别。 package com.lgr.test; public class Student implements Cloneable { private String name; private int age; public...
  • Java 浅拷贝和深拷贝

    2021-01-17 14:08:46
    比如说对象 A 对象 B,二者都是 ClassC 的对象,具有成员变量 a b,现在对对象 A 进行拷贝赋值给 B,也就是 B.a = A.a; B.b = A.b;这时再去改变 B 的属性 a 或者 b 时,可能会遇到问题:假设 a 是基础数据类型...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 33,507
精华内容 13,402
关键字:

java深拷贝和浅拷贝

java 订阅