Java抽象/接口讲解(第四节)Clonable接口/深拷贝和浅拷贝

在 Java 中,Cloneable 接口clone() 方法 是用于实现对象拷贝的机制。通过拷贝对象,你可以创建一个对象的副本,而不必手动复制每一个字段。Cloneable 是一个标记接口,用于表示该类支持对象的浅拷贝。但在实际开发中,除了浅拷贝,有时我们还需要深拷贝


1. Cloneable 接口与 clone() 方法

Cloneable 接口
  • Cloneable 是一个标记接口,这意味着它没有任何方法,只是用来标记一个类支持对象拷贝。如果一个类实现了 Cloneable 接口,则表示该类的对象可以被合法地克隆
  • 如果你不实现 Cloneable 接口,调用 clone() 方法时会抛出 CloneNotSupportedException 异常。
clone() 方法
  • clone() 方法定义在 Object 类中,默认实现的是浅拷贝。这意味着拷贝的对象中的所有基本数据类型都会被复制,而引用类型(对象引用)只复制它们的引用地址。
示例:实现 Cloneable 接口
class Person implements Cloneable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 实现 clone 方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();  // 调用父类的 clone() 方法
    }

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

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("Alice", 30);
        Person person2 = (Person) person1.clone();  // 浅拷贝

        System.out.println("person1: " + person1);
        System.out.println("person2: " + person2);
    }
}
person1: Person{name='Alice', age=30}
person2: Person{name='Alice', age=30}
解释
  • 这里我们实现了 Cloneable 接口,并覆盖了 clone() 方法。调用 super.clone() 执行浅拷贝,person1person2 拥有相同的内容,但它们是两个不同的对象。

2. 浅拷贝(Shallow Copy)VS 深拷贝(Deep Copy)

2.1 浅拷贝(Shallow Copy)

浅拷贝是指:拷贝对象时,基本数据类型的字段会被复制,而引用类型的字段会拷贝引用地址。这意味着新对象和原对象共享相同的引用类型字段。如果引用类型对象被修改,那么两个对象都会受到影响。

示例:浅拷贝的问题
class Address {
    String city;

    public Address(String city) {
        this.city = city;
    }
}

class Person implements Cloneable {
    String name;
    int age;
    Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();  // 浅拷贝
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + ", city=" + address.city + "}";
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", 30, address);
        Person person2 = (Person) person1.clone();  // 浅拷贝

        System.out.println("person1: " + person1);
        System.out.println("person2: " + person2);

        // 修改 person2 的地址,person1 的地址也会改变
        person2.address.city = "Los Angeles";

        System.out.println("After modifying person2's city:");
        System.out.println("person1: " + person1);
        System.out.println("person2: " + person2);
    }
}
person1: Person{name='Alice', age=30, city=New York}
person2: Person{name='Alice', age=30, city=New York}
After modifying person2's city:
person1: Person{name='Alice', age=30, city=Los Angeles}
person2: Person{name='Alice', age=30, city=Los Angeles}
解释
  • 在浅拷贝中,address 是一个引用类型,它的地址被拷贝到了 person2。当我们修改 person2address.city 时,person1address.city 也被修改了。这说明两个对象共享相同的地址引用,导致数据不独立。

2.2 深拷贝(Deep Copy)

深拷贝是指:不仅复制对象的基本数据类型,还要递归地复制引用类型字段,确保所有引用类型的对象都被完全复制,而不是共享引用。这样拷贝出来的对象完全独立,不受原对象的影响。

实现深拷贝的方法:
  • 实现 clone() 方法时,手动调用引用对象的 clone() 方法
  • 使用序列化反序列化的方式进行深拷贝。

示例:实现深拷贝

我们在 Personclone() 方法中,不仅要复制 Person 的基本字段,还要调用 Addressclone() 方法,确保 address 字段也被深拷贝。

class Address implements Cloneable {
    String city;

    public Address(String city) {
        this.city = city;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();  // 浅拷贝
    }
}

class Person implements Cloneable {
    String name;
    int age;
    Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        // 调用父类的clone方法创建浅拷贝
        Person clonedPerson = (Person) super.clone();
        // 对引用类型进行深拷贝
        clonedPerson.address = (Address) this.address.clone();
        return clonedPerson;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + ", city=" + address.city + "}";
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", 30, address);
        Person person2 = (Person) person1.clone();  // 深拷贝

        System.out.println("person1: " + person1);
        System.out.println("person2: " + person2);

        // 修改 person2 的地址,person1 的地址不会改变
        person2.address.city = "Los Angeles";

        System.out.println("After modifying person2's city:");
        System.out.println("person1: " + person1);
        System.out.println("person2: " + person2);
    }
}
总结深拷贝的含义
  1. super.clone():调用 Object 类的 clone() 方法,对当前对象执行浅拷贝,复制所有基本类型字段和引用类型的引用。

  2. clonedPerson.address = (Address) this.address.clone():对引用类型 address 进行深拷贝,确保克隆后的对象和原对象引用不同的 address 对象。

  3. return clonedPerson:返回深拷贝后的新对象。

person1: Person{name='Alice', age=30, city=New York}
person2: Person{name='Alice', age=30, city=New York}
After modifying person2's city:
person1: Person{name='Alice', age=30, city=New York}
person2: Person{name='Alice', age=30, city=Los Angeles}

3. 浅拷贝 VS 深拷贝的比较

  • Cloneable 接口用于指示一个类支持克隆操作,必须配合 clone() 方法使用。
  • 浅拷贝会复制基本数据类型,但引用类型只复制地址,导致多个对象共享同一个引用类型字段,修改一个会影响另一个。
  • 深拷贝递归地复制引用类型字段,确保拷贝出的对象与原对象完全独立。
  • 实现深拷贝时,需要在 clone() 方法中手动调用引用对象的 clone(),或者使用序列化和反序列化的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值