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方法。克隆对象person2的address字段与原始对象person1共享同一个引用对象。因此,修改person2的address字段会影响到person1的address字段。

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字段进行深拷贝,确保person1和person2拥有独立的引用对象。

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

另一种实现深拷贝的方法是使用序列化和反序列化。此方法适用于对象及其所有字段都实现了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对象的深拷贝。序列化将对象转换为字节流,反序列化将字节流转换为对象,从而创建对象的深拷贝。

相关推荐
阿里嘎多学长3 分钟前
2026-10-13 GitHub 热点项目精选
开发语言·程序员·github·代码托管
砚底藏山河6 分钟前
量化新手误区:因子计算与行情数据的工程化解耦
java·数据库·python·金融
威哥爱编程8 分钟前
【AI全栈12-07】Spring Boot 用 Agent 自动完成多步运营任务
java·spring boot·后端
巧克力味的桃子10 分钟前
复杂网络能控性鲁棒性研究进展
开发语言·网络·php
未秃头的程序猿12 分钟前
我用 Spring AI + MCP 把公司内部系统接给了 AI,过程没想象中顺利
java·后端
m0_5873830025 分钟前
碰碰好评卡功能落地实战 从需求梳理到上线部署全流程实操指南
java·linux·服务器·前端·javascript·vue.js·人工智能
普通人631 分钟前
php处理百度编辑器有误html
开发语言·编辑器·html·php·1024程序员节
沉下心来学鲁班39 分钟前
具身智能入门:用 Python 让 MicroDuck 机器狗学会走路
开发语言·人工智能·python·机器人
geovindu40 分钟前
rust: Chain of Responsibility Pattern (续)
开发语言·后端·设计模式·rust·责任链模式·行为模式
诚心呈意共享外卖配送系统41 分钟前
多平台订单聚合系统的技术架构:接 API 只是开始
java·开发语言·架构·系统架构·创业创新