Java基础-(深拷贝 vs 浅拷贝)

91 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

深拷贝 vs 浅拷贝

  1. 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。(浅拷贝不是说只是像下面这样,而是也创建了一个新的对象,如下面的默认的clone方法一样)

    1. Employee original = new Employee("John Public", 50000);
      Employee copy = original;
      
  2. 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

Cloneable接口

如果希望 copy 是一个新对象,它的初始状态与 original 相同, 但是之后它们各自会有自己不同的状态, 这种情况下就可以使用 clone 方法。

要想调用对象的clone方法,那么该对象对应的类应该实现Cloneable接口,这是一个标记接口,不实现则会报错,在java中Object类提供的clone方法默认是浅拷贝,并且clone是Object类中的一个 protected 方法,这说明你的代码不能 直接调用这个方法。只有 Employee 类可以克隆 Employee 对象。这个限制是有原因的。想想看 Object 类如何实clone。它对于这个对象一无所知,不能保证子类的实现者一定会修正 clone 方法让它正常工作, 所以只能对这个对象逐个域地进行拷贝。 如果对象中的所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题、 但是如果对象包含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来,原对象和克隆的对象仍然会共享一些信息。所以这样一来,其实默认的clone方法是如下这样的。(这里的original 和copy是两个不同的对象来的,即if(copy == original){System.out.println(1);}不会输出

浅拷贝会有什么影响吗? 这要看具体情况。如果原对象和浅克隆对象共享的子对象是不可变的, 那么这种共享就是安全的。如果子对象属于一个不可变的类, 如 String, 就 是 这 种情况。或者在对象的生命期中, 子对象一直包含不变的常量, 没有更改器方法会改变它, 也没有方法会生成它的引用,这种情况下同样是安全的。不过, 通常子对象都是可变的, 必须重新定义 clone 方法来建立一个深拷贝, 同时克隆所有子对象。在这个例子中,hireDay 域是一个 Date , 这是可变的, 所以它也需要克隆。

Cloneable 实现深拷贝

要实现深拷贝,则必须在实现 Cloneable 接口的基础上还要,重新定义 clone 方法,并指定 public 访问修饰符以方便在别的类调用。

public class Employee implements Cloneable
 {
 		//省略get,set
         private String name;
         private double salary;
         private Date hireDay;
	public Employee clone() throws CloneNotSupportedException
    {
        // call Object,clone0
        Employee cloned = (Employee) super.clone() ;
        // clone mutable(可变的) fields,Date类中已经实现了clone方法了!所以在这里可以直接调用!
        cloned.hireDay = (Date) hireDay.clone();
        return cloned;
    }

}   

如果在一个对象上调用 clone, 但这个对象的类并没有实现 Cloneable 接口, Object 类的 clone 方法就会拋出一个 CloneNotSupportedExceptionD 当然,Employee 和 Date 类实现了Cloneable 接口,所以不会抛出这个异常。 不过, 编译器并不了解这一点,因此,我们在上面代码中声明了这个异常。