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

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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容