java-克隆概述

Java中的克隆

1. 克隆的基本概念

在Java中,克隆是一种创建对象副本的方法。克隆允许你复制对象,而不是通过构造函数创建新的对象实例。Java提供了两种主要的克隆方式:浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。理解这两种方式的区别对于正确实现对象的克隆非常重要。

2. Cloneable接口

Cloneable接口是Java标准库中的一个标记接口。一个实现了Cloneable接口的类,其实例可以调用clone方法来创建对象的副本。如果类没有实现Cloneable接口,那么调用clone方法时会抛出CloneNotSupportedException异常。需要注意的是,Cloneable接口本身并没有定义任何方法,它的存在只是标志着实现这个接口的类支持克隆操作。

3. Object类的clone方法

Object类提供了一个受保护的clone方法,这个方法返回对象的一个副本。要使用这个方法,必须在子类中覆盖它,并将其可见性提高到public。默认的clone方法执行的是浅拷贝。

clone方法的签名
复制代码
protected native Object clone() throws CloneNotSupportedException;

4. 浅拷贝与深拷贝

4.1 浅拷贝

浅拷贝复制对象的基本类型字段和引用类型字段的引用,而不复制引用对象本身。这意味着原始对象和克隆对象共享同一个引用对象。

复制代码
class Address {
    String city;

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

class Person implements Cloneable {
    String name;
    Address address;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            Address address = new Address("New York");
            Person person1 = new Person("John", address);
            Person person2 = (Person) person1.clone();

            System.out.println("Person1 Address: " + person1.address.city); // 输出:New York
            System.out.println("Person2 Address: " + person2.address.city); // 输出:New York

            person2.address.city = "Los Angeles";
            System.out.println("Person1 Address after modification: " + person1.address.city); // 输出:Los Angeles
            System.out.println("Person2 Address after modification: " + person2.address.city); // 输出:Los Angeles
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,Person类实现了Cloneable接口并重写了clone方法。克隆对象person2address字段与原始对象person1共享同一个引用对象。因此,修改person2address字段会影响到person1address字段。

4.2 深拷贝

深拷贝不仅复制对象的基本类型字段,还递归地复制引用类型字段所引用的对象。这意味着原始对象和克隆对象拥有独立的引用对象。

实现深拷贝的常见方法包括:手动实现clone方法,使用序列化和反序列化。

手动实现深拷贝
复制代码
class Address implements Cloneable {
    String city;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable {
    String name;
    Address address;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = (Address) address.clone();
        return cloned;
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            Address address = new Address("New York");
            Person person1 = new Person("John", address);
            Person person2 = (Person) person1.clone();

            System.out.println("Person1 Address: " + person1.address.city); // 输出:New York
            System.out.println("Person2 Address: " + person2.address.city); // 输出:New York

            person2.address.city = "Los Angeles";
            System.out.println("Person1 Address after modification: " + person1.address.city); // 输出:New York
            System.out.println("Person2 Address after modification: " + person2.address.city); // 输出:Los Angeles
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,Person类的clone方法首先调用super.clone方法进行浅拷贝,然后对address字段进行深拷贝,确保person1person2拥有独立的引用对象。

使用序列化和反序列化实现深拷贝

另一种实现深拷贝的方法是使用序列化和反序列化。此方法适用于对象及其所有字段都实现了Serializable接口。

复制代码
import java.io.*;

class Address implements Serializable {
    String city;

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

class Person implements Serializable {
    String name;
    Address address;

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

    public Object deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(this);

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        return ois.readObject();
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            Address address = new Address("New York");
            Person person1 = new Person("John", address);
            Person person2 = (Person) person1.deepClone();

            System.out.println("Person1 Address: " + person1.address.city); // 输出:New York
            System.out.println("Person2 Address: " + person2.address.city); // 输出:New York

            person2.address.city = "Los Angeles";
            System.out.println("Person1 Address after modification: " + person1.address.city); // 输出:New York
            System.out.println("Person2 Address after modification: " + person2.address.city); // 输出:Los Angeles
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,通过序列化和反序列化实现了Person对象的深拷贝。序列化将对象转换为字节流,反序列化将字节流转换为对象,从而创建对象的深拷贝。

相关推荐
小羊没烦恼!2 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里2 天前
java中的继承和多态的区别
java
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕2 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码2 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖2 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时2 天前
01-06-A-JVM排查实战详解
java·jvm
伞伞悦读2 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
vipxieliang2 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
C语言小火车2 天前
C/C++ 为什么需要编译器?
开发语言·c++