zoukankan      html  css  js  c++  java
  • 对象复制

    前言

      在前面的文章中,提到过Java之间如果发生了对象赋值,那么其意义是赋值的两个对象都指向同一片内存区域。

      那么,如果我希望得到的是一份新的副本 - 即可以随意更改而不影响原始对象呢?

      那就涉及到本文要探讨的话题 - 对象的浅拷贝与深拷贝。

    浅拷贝

      若对象之间发生浅拷贝,那么首先肯定的是会创建一个新的对象副本(这就不同与对象间的直接赋值).

      然后所有域进行简单的直接复制 - 非对象域直接拷贝过来,对象域则拷贝此对象的地址(因为对象在java中的本质就是一个 "指针")

    如何进行浅拷贝

      1. 为目标类继承Cloneable接口。

      2. 使用public访问修辞符重新定义clone方法且抛出CloneNotSupportedException异常。

      3. 在clone方法中调用父类的clone方法并将结果强制转型为当前类类型后返回。

    深拷贝

      看完了浅拷贝,你很有可能对其处理对象域的操作不满意。因为对象如果直接拷贝地址过来,则副本中该对象域的改变势必也会影响到原始对象中的对象域。

      相对于浅拷贝,深拷贝是指用户自定义对象域的拷贝方式。

    如何进行深拷贝

      1. 为目标类继承Cloneable接口。

      2. 使用public访问修辞符重新定义clone方法且抛出CloneNotSupportedException异常。

      3. 在clone方法中调用父类的clone方法并将结果强制转型为当前类类型。

      4. 对3中处理得到的结果以自定义的方式拷贝对象域部分。

    代码示例 - 直接赋值

      测试类:

     1 package test;
     2 
     3 // 测试类A
     4 public class A {
     5     
     6     // 打印对象内的整型变量值
     7     public void showValueInt() {
     8         System.out.println(valueInt);
     9     }
    10     // 打印对象内的字符串变量值
    11     public void showValueStr() {
    12         System.out.println(valueStr);
    13     }
    14     // 设置对象内的整型变量值
    15     public boolean setValue (int valueInt) {
    16         this.valueInt = valueInt;
    17         return true;
    18     }
    19     // 设置对象内的字符串变量值
    20     public boolean setValue (String valueStr) {
    21         this.valueStr = valueStr;
    22         return true;
    23     }
    24     
    25     // 值1
    26     private int valueInt;
    27     // 值2
    28     private String valueStr;
    29 }

      测试代码:

     1 package test;
     2 
     3 public class Java7Learn {
     4     
     5     public static void main(String[] args){
     6         
     7         // 创建一个对象a1,并让它直接赋值给a2。
     8         A a1 = new A();
     9         A a2 = a1;
    10         
    11         // 设置a1的两个变量值并打印出来
    12         a1.setValue(1);
    13         a1.setValue("this is a1");
    14         a1.showValueInt();
    15         a1.showValueStr();
    16         
    17         // 设置a2的两个变量值并打印出来
    18         a2.setValue(2);
    19         a2.setValue("this is a2");
    20         a2.showValueInt();
    21         a2.showValueStr();
    22         
    23         // 显示结果发现 - a1的对象值都变成a2的了。
    24         a1.showValueInt();
    25         a1.showValueStr();
    26     }    
    27 }

      运行结果

      

    代码示例 - 浅拷贝

      首先按照前文所讲的浅拷贝的方法修改测试类:

     1 package test;
     2 
     3 // 测试类A
     4 public class A implements Cloneable {
     5     
     6      // 实现clone浅拷贝方法
     7     public A clone() throws CloneNotSupportedException
     8     {
     9         return (A)super.clone();
    10     }
    11     // 打印对象内的整型变量值
    12     public void showValueInt() {
    13         System.out.println(valueInt);
    14     }
    15     // 打印对象内的字符串变量值
    16     public void showValueStr() {
    17         System.out.println(valueStr);
    18     }
    19     // 设置对象内的整型变量值
    20     public boolean setValue (int valueInt) {
    21         this.valueInt = valueInt;
    22         return true;
    23     }
    24     // 设置对象内的字符串变量值
    25     public boolean setValue (String valueStr) {
    26         this.valueStr = valueStr;
    27         return true;
    28     }
    29     
    30     // 值1
    31     private int valueInt;
    32     // 值2
    33     private String valueStr;
    34 }

      测试代码:

    package test;
    
    public class Java7Learn {
        
        public static void main(String[] args) throws CloneNotSupportedException{
            
            // 创建一个对象a1,并将它克隆到a2。
            A a1 = new A();
            A a2 = a1.clone();
            
            // 设置a1的两个变量值并打印出来
            a1.setValue(1);
            a1.setValue("this is a1");
            a1.showValueInt();
            a1.showValueStr();
            
            // 设置a2的两个变量值并打印出来
            a2.setValue(2);
            a2.setValue("this is a2");
            a2.showValueInt();
            a2.showValueStr();
            
            // 显示结果发现 - a1的整型对象值没变,但字符串型的变了。
            a1.showValueInt();
            a1.showValueStr();
        }    
    }

      运行结果

      

      发现,a1的整型对象值没变,但字符串型的变了。这是和预期相一致的。

    代码示例 - 深拷贝

      首先按照前文所讲的深拷贝的方法修改测试类:

     1 package test;
     2 
     3 // 测试类A
     4 public class A implements Cloneable {
     5     
     6     // 实现clone深拷贝方法
     7     public A clone() throws CloneNotSupportedException
     8     {
     9         A cloned = (A)super.clone();
    10         // 自定义拷贝方式。
    11         // 一般定义的方式都是调用成员自己的clone函数。但测试的字符串默认赋值会深拷贝,就没有在这里继续clone了。
    12         cloned.valueStr = valueStr;
    13         return cloned;
    14     }
    15     // 打印对象内的整型变量值
    16     public void showValueInt() {
    17         System.out.println(valueInt);
    18     }
    19     // 打印对象内的字符串变量值
    20     public void showValueStr() {
    21         System.out.println(valueStr);
    22     }
    23     // 设置对象内的整型变量值
    24     public boolean setValue (int valueInt) {
    25         this.valueInt = valueInt;
    26         return true;
    27     }
    28     // 设置对象内的字符串变量值
    29     public boolean setValue (String valueStr) {
    30         this.valueStr = valueStr;
    31         return true;
    32     }
    33     
    34     // 值1
    35     private int valueInt;
    36     // 值2
    37     private String valueStr;
    38 }

      测试代码:

     1 package test;
     2 
     3 public class Java7Learn {
     4     
     5     public static void main(String[] args) throws CloneNotSupportedException{
     6         
     7         // 创建一个对象a1,并将它克隆到a2。
     8         A a1 = new A();
     9         A a2 = a1.clone();
    10         
    11         // 设置a1的两个变量值并打印出来
    12         a1.setValue(1);
    13         a1.setValue("this is a1");
    14         a1.showValueInt();
    15         a1.showValueStr();
    16         
    17         // 设置a2的两个变量值并打印出来
    18         a2.setValue(2);
    19         a2.setValue("this is a2");
    20         a2.showValueInt();
    21         a2.showValueStr();
    22         
    23         // 显示结果发现 - a1的所有对象值都没被影响。
    24         a1.showValueInt();
    25         a1.showValueStr();
    26     }    
    27 }

      运行结果

      

      a2所做的操作完全没有影响a1的域。

    小结

      1. 要清晰区分对象直接赋值,浅拷贝,深拷贝这三者的底层机制。

      2. clone的方式确实有点 "笨重",但必须严格的遵守才能写出高质量的代码。

  • 相关阅读:
    9、Spring Boot 2.x 集成 Thymeleaf
    【专题】Spring Boot 2.x 面试题
    8、Spring Boot 2.x 服务器部署
    7、Spring Boot 2.x 集成 Redis
    6、Spring Boot 2.x 集成 MyBatis
    5、Spring Boot 2.x 启动原理解析
    4、Spring Boot 2.x 自动配置原理
    3、Spring Boot 2.x 核心技术
    2、Spring Boot 2.x 快速入门
    centOS下安装JDK1.8.60,glassfish4.1.1以及MySQL
  • 原文地址:https://www.cnblogs.com/scut-fm/p/4150225.html
Copyright © 2011-2022 走看看