Cloneable接口-深拷贝与浅拷贝

Cloneable接口-深拷贝与浅拷贝

  • Cloneable接口源码
  • 浅拷贝案例
    • Pet类定义
    • Person类定义
    • 浅拷贝问题-代码测试
  • 深拷贝案例
    • Pet类重写clone()方法
    • Person的clone()方法中调用Pet的clone方法
    • 浅拷贝问题解决-深拷贝代码测试

    如果接触过C++的指针与引用类型的对象复制操作,那么对于“浅拷贝和深拷贝”的概念理解起来十分容易,实质上就是:类的对象的某个属性,在堆区的内存——是共享同一块或者是独自占有一块的问题
    可以参考知乎上的文章:《深入浅出| java中的clone方法》做深入了解。

Cloneable接口源码

    Cloneable接口:
        实现此接口的类——可被推断java.lang.Object的clone()方法可以被合法调用-以实现类实例:属性到属性的拷贝。
        如果一个类未实现Cloneable接口,那么调用clone()方法时,会抛出CloneNotSupportedException异常。
        通常,实现了Cloneable接口的子类,应当以public访问权限重写clone()方法(尽管java.Object类中的clone方法是protected类型的)
        应当认识到:Cloneable接口没有包含clone()方法,因此,如果仅仅是implements了Cloneable接口,那么也是无法正常克隆对象的
         [原因:即使是反射性地调用了克隆方法,也不能保证它一定会成功]——个人理解就是:是否重写Clone()方法、或者“浅拷贝与深拷贝”问题的存在所导致的。

package java.lang;
/**
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

浅拷贝案例

Pet类定义

    注意到:Pet类实现了Cloneable接口,但是没有重写Clone()方法(显然:此时Pet类不具备对象克隆的能力)。

class Pet implements Cloneable{
    //properties
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Pet() {
    }

    public Pet(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pet pet = (Pet) o;
        return Objects.equals(name, pet.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

//    @Override
//    public Pet clone() {
//        try {
//            return (Pet)super.clone();
//        } catch (CloneNotSupportedException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }
}

Person类定义

    注意到:Person类实现了Cloneable接口,也重写Clone()方法。那么,是否Person类就具备了对象克隆的能力呢?(由于浅拷贝问题的存在,认为这种对象克隆能力是不完整的、有缺陷的)。

class Person implements Cloneable {
    //properties
    private String name;
    private Integer age;
    private Pet pet;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setPet(Pet pet) {
        this.pet = pet;
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public Pet getPet() {
        return pet;
    }

    public Person(){
        this(null,null,null);
    }
    public Person(String name,Integer age,Pet pet){
        this.name=name;
        this.age=age;
        this.pet=pet;
    }

    @Override
    public Person clone() {
        Person person=null;
        try {
            //浅拷贝[此时-仅仅是通过地址引用的方式获取到Pet对象在堆内存的地址,即:原对象和拷贝后的对象pet属性共享堆区地址]
           person = (Person) super.clone();
//           //深拷贝[此时-原对象pet属性、拷贝后的对象pet属性分别占有不同的堆区地址]
//           person.pet = pet.clone();
           return person;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", pet=" + pet +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
                Objects.equals(age, person.age) &&
                Objects.equals(pet, person.pet);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, pet);
    }
}

浅拷贝问题-代码测试

    为什么说:此时Person类的对象克隆能力是不完整的、有缺陷的?因为此时,在通过Person对象调用clone()方法,克隆对象时,其成员属性pet(Pet类的对象)值的克隆,仅仅是对堆区内存地址的简单拷贝。
    即:说白了,Person对象和克隆出来的对象,其pet属性值共享同一块堆区内存。——问题显而易见:当对克隆出来的对象的pet属性进行set操作时,显然会影响到原始Person对象的pet属性值。
    代码演示如下:

  //methods
    public static void main(String[] args) throws CloneNotSupportedException {
        testPerson();
    }
    public static void testPerson() throws CloneNotSupportedException {
        Person p=new Person("张三",14,new Pet("小黑"));
        System.out.println(p);
        Person clone = (Person)p.clone();
        System.out.println(clone);
        System.out.println(p.equals(clone));
        System.out.println(p.getPet()==clone.getPet());
        System.out.println("************");
        clone.setAge(15);
        System.out.println(p);
        System.out.println(clone);
        System.out.println(p.equals(clone));
        System.out.println("************");
        clone.getPet().setName("小黄");
        System.out.println(p);
        System.out.println(clone);
        System.out.println(p.equals(clone));
        System.out.println(p.getPet()==clone.getPet());

    }

Cloneable接口-深拷贝与浅拷贝_第1张图片

深拷贝案例

    那么,如何实现深拷贝呢?关键就在上述案例中,被注释的几行代码中。

Pet类重写clone()方法

Cloneable接口-深拷贝与浅拷贝_第2张图片

Person的clone()方法中调用Pet的clone方法

Cloneable接口-深拷贝与浅拷贝_第3张图片

浅拷贝问题解决-深拷贝代码测试

    测试代码不变,再次运行:
Cloneable接口-深拷贝与浅拷贝_第4张图片

你可能感兴趣的:(Java,浅拷贝,深拷贝,Cloneable接口)