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

相关推荐
SoFlu软件机器人2 分钟前
Go/Rust 疯狂蚕食 Java 市场?老牌语言的 AI 化自救之路
java·golang·rust
半盏茶香5 分钟前
启幕数据结构算法雅航新章,穿梭C++梦幻领域的探索之旅——堆的应用之堆排、Top-K问题
java·开发语言·数据结构·c++·python·算法·链表
hweiyu0013 分钟前
idea如何让打开的文件名tab多行显示
java·ide·intellij-idea·idea·intellij idea
小吴先生66626 分钟前
Groovy 规则执行器,加载到缓存
java·开发语言·缓存·groovy
星星不打輰36 分钟前
Spring基于注解进行开发
java·spring
陈大爷(有低保)37 分钟前
Spring中都用到了哪些设计模式
java·后端·spring
秋风&萧瑟41 分钟前
【QT】QT的多界面跳转以及界面之间传递参数
开发语言·qt
骑牛小道士44 分钟前
JAVA- 锁机制介绍 进程锁
java·开发语言
郭涤生1 小时前
Chapter 1: Historical Context_《C++20Get the details》_notes
开发语言·c++20
高林雨露1 小时前
Java对比学习Kotlin的详细指南(一)
java·学习·kotlin