美文网首页java
Java 对象克隆、深拷贝、浅拷贝

Java 对象克隆、深拷贝、浅拷贝

作者: J先生有点儿屁 | 来源:发表于2018-08-07 17:07 被阅读42次

    Java 对象克隆、深拷贝、浅拷贝

    背景

    前一阵子在测试的时候,开发小哥因为需要缓存一个比较常用的对象,故此保存了一份;但又因为业务需要这份对象需要做一些改变。
    因为开发小哥没有考虑到这个业务需求,导致对象每次在进行业务修改后,下个用户请求进来会是以上次用户请求的对象返回,造成了这样的一个bug。
    后来,开发小哥用了深拷贝的方式重新复制了一个对象。

    故此,顺带再回顾下Java中对象克隆、深拷贝、浅拷贝。
    在实际开发工作中会出现如下场景,当A对象在某业务场景下需要克隆出一个一模一样的对象,同时新克隆出来的对象与原有对象不能有关系。要求新旧对象是两个长的对象,但是两个完全独立的对象。

    一、对象克隆

    在Java中,用简单赋值语句无法满足对象克隆的需求,要满足上诉需求有很多途径,但实际上可以通过clone()方法最简单最高效的解决。
    总所周知Java所有对象的父类是java.lang.Object类,同时Object类中有一个clone()方法。如下:

    protected native Object clone() throws CloneNotSupportedException;
    
      1. 注意观察该方法,是一个native方法。一般说来native方法的效率要远远高于Java非native方法。
      1. 同时该方法是protected受保护的方法。因为所有的对象自身就是继承于Object类,所以这个方法自然存在。
      1. 为了实现自定义的clone功能,则需要重写clone方法。但如果要让其他类能够调用到clone方法,则必须将clone方法属性设置为public。
        为了达到clone方法能够使用的底部,Java提供了java.lang.Cloneable接口,接口如下:
      public interface Cloneable {
      }
      

    Cloneable接口不包含任何方法!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类clone()方法的,如果需要克隆的类没有Cloneable接口,同时又调用了Object的clone()方法,则Object的clone()方法就会抛出CloneNotSupportedException异常。
    例如:

    CloneNotSupporedException异常

    二、深拷贝与浅拷贝

    浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。举例来说更加清楚:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。

    以下是本文的类结构:


    Car类
    Student类中成员变量有Car类

    Car类和Student类都实现了Cloneable接口,同时Student类中成员变量有Car类。

    (一)浅拷贝

    若不对clone()方法进行改写,则调用此方法得到的对象即为浅拷贝,下面我们着重谈一下深拷贝。运行下面的程序,看一看浅拷贝。

    • Car类
      public class Car implements Cloneable {
          private String color;
          private int price;
      
          public Car(String color, int price) {
              this.color = color;
              this.price = price;
          }
      
          public String getColor() {
              return color;
          }
      
          public void setColor(String color) {
              this.color = color;
          }
      
          public int getPrice() {
              return price;
          }
      
          public void setPrice(int price) {
              this.price = price;
          }
      
          @Override
          protected Object clone() {
              Car car = null;
              try {
                  car = (Car) super.clone();
              } catch (CloneNotSupportedException e) {
                  e.printStackTrace();
              }
              return car;
          }
      
          @Override
          public String toString() {
              return "Car{" +
                      "color='" + color + '\'' +
                      ", price=" + price +
                      '}';
          }
      }
      
    • Student类
      public class Student implements Cloneable {
          private String name;
          private int age;
          private Car car;
      
          public Student(String name, int age, Car car) {
              this.name = name;
              this.age = age;
              this.car = car;
          }
      
          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 Car getCar() {
              return car;
          }
      
          public void setCar(Car car) {
              this.car = car;
          }
      
          @Override
          protected Object clone() {
              Student student = null;
              try {
                  student = (Student) super.clone();
              } catch (CloneNotSupportedException e) {
                  e.printStackTrace();
              }
              return student;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", car=" + car +
                      '}';
          }
      }
      
    • 测试类
      public class TestClone {
          @Test
          public void testName() {
              Car car = new Car("red", 111);
              Student s1 = new Student("a", 12, car);
              Student s2 = (Student) s1.clone();
              System.out.println(s1);
              System.out.println(s2);
              System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~");
              s2.getCar().setColor("blue");
              s2.getCar().setPrice(110);
              s2.setAge(2);
              s2.setName("dw");
              System.out.println(s1);
              System.out.println(s2);
          }
      }
      
    • 运行结果:
      Student{name='a', age=12, car=Car{color='red', price=111}}
      Student{name='a', age=12, car=Car{color='red', price=111}}
      ~~~~~~~~~~~~~~~~~~~~~~~~~~
      Student{name='a', age=12, car=Car{color='blue', price=110}}
      Student{name='dw', age=2, car=Car{color='blue', price=110}}
      

    从结果上可以看出,当s2设置了car的值后,s1的car也随着改变了。可知浅拷贝只克隆了最外一层类的对象,而其他并未克隆。

    (二)深拷贝

    对此,将Student类进行了修改达到深拷贝的结果。调整后的Student类如下:
    ```
    public class Student implements Cloneable {
    private String name;
    private int age;
    private Car car;

        public Student(String name, int age, Car car) {
            this.name = name;
            this.age = age;
            this.car = car;
        }
    
        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 Car getCar() {
            return car;
        }
    
        public void setCar(Car car) {
            this.car = car;
        }
    
        @Override
        protected Object clone() {
            Student student = null;
            try {
                student = (Student) super.clone();
    // 如果该类里有其他类的成员变量是引用类型,则将引用类型的对象也进行克隆操作。
                Car car = (Car) student.getCar().clone();
                student.setCar(car);
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return student;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", car=" + car +
                    '}';
        }
    }
    ```
    
    • 运行结果:
      Student{name='a', age=12, car=Car{color='red', price=111}}
      Student{name='a', age=12, car=Car{color='red', price=111}}
      ~~~~~~~~~~~~~~~~~~~~~~~~~~
      Student{name='a', age=12, car=Car{color='red', price=111}}
      Student{name='dw', age=2, car=Car{color='blue', price=110}}
      

    运行结果会发现,s2中car的改变不影响s1的car值。

    浅拷贝只是进行了目标对象的拷贝,并没有对对象中的引用类型变量也进行拷贝;
    深拷贝恰恰是将目标对象克隆,同时也对目标对象中的引用类型变量同时进行了克隆,如果引用对象中还存在引用对象,则也一并进行克隆,最后达到深度克隆的效果。

    参考

    https://www.cnblogs.com/xuanxufeng/p/6558330.html

    相关文章

      网友评论

        本文标题:Java 对象克隆、深拷贝、浅拷贝

        本文链接:https://www.haomeiwen.com/subject/eqjavftx.html