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对象的深拷贝。序列化将对象转换为字节流,反序列化将字节流转换为对象,从而创建对象的深拷贝。

相关推荐
LallanaLee几秒前
常见面试题
java·开发语言
酷炫码神5 分钟前
C#运算符
开发语言·c#
小秋学嵌入式-不读研版9 分钟前
C42-作业练习
c语言·开发语言·笔记
休息一下接着来16 分钟前
C++ 条件变量与线程通知机制:std::condition_variable
开发语言·c++·算法
爱尚你199326 分钟前
Java 泛型与类型擦除:为什么解析对象时能保留泛型信息?
java
小哈里36 分钟前
【pypi镜像源】使用devpi实现python镜像源代理(缓存加速,私有仓库,版本控制)
开发语言·python·缓存·镜像源·pypi
努力学习的小廉40 分钟前
【C++】 —— 笔试刷题day_29
开发语言·c++·算法
电商数据girl1 小时前
酒店旅游类数据采集API接口之携程数据获取地方美食品列表 获取地方美餐馆列表 景点评论
java·大数据·开发语言·python·json·旅游
天天打码1 小时前
python版本管理工具-pyenv轻松切换多个Python版本
开发语言·python
CircleMouse1 小时前
基于 RedisTemplate 的分页缓存设计
java·开发语言·后端·spring·缓存