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 分钟前
11.AOP开发
java·spring boot·spring·springboot3
_修铁路的2 分钟前
解决 EasyExcel 填充图片占满单元格问题
java·easyexcel
Yhame.16 分钟前
【使用层次序列构建二叉树(数据结构C)】
c语言·开发语言·数据结构
HanhahnaH18 分钟前
Spring集合注入Bean
java·spring
言之。23 分钟前
【Go语言】RPC 使用指南(初学者版)
开发语言·rpc·golang
未定义.22124 分钟前
电子削铅笔刀顺序图详解:从UML设计到PlantUML实现
java·软件工程·uml
雾月5541 分钟前
LeetCode 1292 元素和小于等于阈值的正方形的最大边长
java·数据结构·算法·leetcode·职场和发展
投笔丶从戎1 小时前
Kotlin Multiplatform--01:项目结构基础
android·开发语言·kotlin
24k小善2 小时前
Flink TaskManager详解
java·大数据·flink·云计算
杜小暑2 小时前
动态内存管理
c语言·开发语言·动态内存管理