深浅克隆学习笔记

Java 中的对象和引用对象的概念与一般的编程语言类似。Java 中的对象是由类定义的,可以通过 new 关键字创建实例,分配内存空间,并返回一个引用对象。这个引用对象指向内存中的实际对象。

下面是一个 Java 的示例代码,展示如何定义对象和引用对象:

java 复制代码
public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

// 创建一个 Person 对象并获取引用对象
Person person1 = new Person("Alice");

// 创建另一个 Person 对象并获取引用对象
Person person2 = new Person("Bob");

// 引用对象的操作
System.out.println(person1.getName());  // 输出:"Alice"
System.out.println(person2.getName());  // 输出:"Bob"

// 修改引用对象的属性
person1.setName("Charlie");
System.out.println(person1.getName());  // 输出:"Charlie"

在上面的示例中,我们定义了一个 Person 类,并创建了两个 Person 对象 person1 和 person2。通过引用对象 person1 和 person2,我们可以访问和修改这两个对象的属性。

需要注意的是,在 Java 中,所有的对象都是通过引用对象来操作的。当我们将一个对象赋值给另一个变量时,实际上是将该对象的引用复制了一份,而不是复制了整个对象。因此,在 Java 中,多个引用对象可以指向同一个实际对象,这意味着它们共享相同的数据和方法。在操作引用对象时,实际上是在操作相同的实际对象。

在 Java 中,对象的克隆是指创建一个与原始对象具有相同状态的新对象。Java 提供了两种不同类型的克隆:浅克隆和深克隆。

浅克隆(Shallow Clone)是指创建一个新对象,该对象与原始对象具有相同的字段值,但是对象的引用类型字段仍然指向原始对象中相同的引用。换句话说,浅克隆只复制对象本身和对象中的基本数据类型字段,而不会复制引用类型字段所引用的对象。

深克隆(Deep Clone)是指创建一个新对象,该对象与原始对象具有相同的字段值,并且对象的引用类型字段也被复制为新的对象。换句话说,深克隆会递归地复制对象及其所有引用类型字段所引用的对象。

简单总结一下,就是:

浅克隆:仅复制所拷贝的对象,不复制引用对象

深克隆:对象和引用对象都复制

Java 中的 Cloneable 接口和 clone() 方法提供了对象克隆的支持。要实现克隆,需要满足以下条件:

  1. 类必须实现 Cloneable 接口
  2. 类必须重写 clone() 方法,并将其访问修饰符设置为 public。

下面是一个示例代码,展示了浅克隆和深克隆的区别:

java 复制代码
class Person implements Cloneable {
    private String name;
    private Address address;

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

    public String getName() {
        return name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

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

class Address {
    private String city;

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

    public String getCity() {
        return city;
    }
}

// 浅克隆示例
Person person1 = new Person("Alice", new Address("New York"));
Person person2 = (Person) person1.clone();

System.out.println(person1.getName());         // 输出:"Alice"
System.out.println(person2.getName());         // 输出:"Alice"
System.out.println(person1.getAddress());      // 输出:Address@<地址>
System.out.println(person2.getAddress());      // 输出:Address@<地址>

person1.getAddress().setCity("Los Angeles");

System.out.println(person1.getAddress().getCity());  // 输出:"Los Angeles"
System.out.println(person2.getAddress().getCity());  // 输出:"Los Angeles"

// 深克隆示例
class Person implements Cloneable {
    private String name;
    private Address address;

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

    public String getName() {
        return name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

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

class Address implements Cloneable {
    private String city;

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

    public String getCity() {
        return city;
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

// 深克隆示例
Person person1 = new Person("Alice", new Address("New York"));
Person person2 = (Person) person1.clone();

System.out.println(person1.getName());         // 输出:"Alice"
System.out.println(person2.getName());         // 输出:"Alice"
System.out.println(person1.getAddress());      // 输出:Address@<地址>
System.out.println(person2.getAddress());      // 输出:Address@<地址>

person1.getAddress().setCity("Los Angeles");

System.out.println(person1.getAddress().getCity());  // 输出:"Los Angeles"
System.out.println(person2.getAddress().getCity());  // 输出:"New York"

在上面的示例中,我们定义了一个 Person 类和一个 Address 类。Person 类包含一个引用类型字段 address,而 Address 类只有一个基本数据类型字段 city。

首先,我们展示了浅克隆的示例。通过调用 clone() 方法创建了 person2,并输出了两个对象的字段值。可以看到,虽然 person2 的名称与 person1 相同,但是它们共享相同的 Address 对象。因此,当我们修改 person1 的地址时,person2 的地址也会相应地发生变化。

然后,我们展示了深克隆的示例。通过在 Person 的 clone() 方法中调用 address 的 clone() 方法,我们创建了一个新的 Address 对象,并将其赋值给克隆的 Person 对象。这样,即使我们修改 person1 的地址,person2 的地址仍然保持不变。

需要注意的是,在进行深克隆时,被克隆的类必须实现 Cloneable 接口,并在 clone() 方法中递归地调用引用类型字段的 clone() 方法。

序列化是指将对象转化为字节流的过程,可以将这些字节保存到文件中、在网络上传输或者在内存中进行存储。通过序列化,我们可以实现对象的持久化和跨网络传输。

在 Java 中,要使一个类可序列化,需要满足以下条件:

  1. 类实现 java.io.Serializable 接口。
  2. 所有非瞬态字段(transient 关键字修饰的字段除外)必须是可序列化的。

接下来,我将给出一个简单的示例来说明如何使用序列化。

java 复制代码
import java.io.*;

class Person implements Serializable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class SerializationExample {
    public static void main(String[] args) {
        // 创建一个对象
        Person person = new Person("Alice", 25);

        // 将对象序列化到文件
        try (FileOutputStream fileOut = new FileOutputStream("person.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut)) {

            out.writeObject(person);
            System.out.println("对象已成功序列化到文件。");

        } catch (IOException e) {
            e.printStackTrace();
        }

        // 从文件中反序列化对象
        try (FileInputStream fileIn = new FileInputStream("person.ser");
             ObjectInputStream in = new ObjectInputStream(fileIn)) {

            Person deserializedPerson = (Person) in.readObject();
            System.out.println("从文件中反序列化的对象名字:" + deserializedPerson.getName());
            System.out.println("从文件中反序列化的对象年龄:" + deserializedPerson.getAge());

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在上面的示例中,我们创建了一个简单的 Person 类,并实现了 Serializable 接口。然后,我们创建一个 Person 对象,并将其序列化到名为 "person.ser" 的文件中。接着,我们从文件中反序列化对象,并输出其名称和年龄。

值得注意的是,在序列化过程中,我们使用了 ObjectOutputStream 将对象写入文件,而在反序列化过程中,我们使用了 ObjectInputStream 从文件中读取对象。

请注意,如果要序列化的类包含了非序列化的字段,则这些字段的值在序列化和反序列化过程中将不会被保存和还原。

通过序列化可以实现深克隆,这是因为序列化会将对象及其引用的所有对象都写入到输出流中,然后再从输入流中读取,从而创建一个全新的对象。

下面是一个示例代码,展示了如何通过序列化实现深克隆:

java 复制代码
class Person implements Serializable {
    private String name;
    private Address address;

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

    public String getName() {
        return name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Person deepClone() throws IOException, ClassNotFoundException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(this);
            try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
                return (Person) ois.readObject();
            }
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

class Address implements Serializable {
    private String city;

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

    public String getCity() {
        return city;
    }
}

// 示例代码的使用方式:
Person person1 = new Person("Alice", new Address("New York"));
Person person2 = person1.deepClone();

System.out.println(person1.getName());         // 输出:"Alice"
System.out.println(person2.getName());         // 输出:"Alice"
System.out.println(person1.getAddress());      // 输出:Address@<地址>
System.out.println(person2.getAddress());      // 输出:Address@<地址>

person1.getAddress().setCity("Los Angeles");

System.out.println(person1.getAddress().getCity());  // 输出:"Los Angeles"
System.out.println(person2.getAddress().getCity());  // 输出:"New York"

在上面的示例中,我们将 Person 类和 Address 类都实现了 Serializable 接口,并在 Person 类中定义了一个 deepClone() 方法。deepClone() 方法使用了序列化的方式实现深克隆。

在 deepClone() 方法中,我们首先将对象写入一个 ByteArrayOutputStream 中,然后通过 ObjectOutputStream 将其写入字节数组。接着,我们将字节数组读取到 ByteArrayInputStream 中,并通过 ObjectInputStream 读取出新的对象。

通过这种方式,我们可以创建一个与原始对象具有相同状态的全新对象,并且所有引用类型字段都会被复制为新的对象。因此,即使我们修改原始对象的引用类型字段,克隆的对象也不会受到影响。

需要注意的是,被克隆的类及其引用类型字段都必须实现 Serializable 接口,否则会抛出 NotSerializableException 异常。此外,还要注意处理可能抛出的 IOException 和 ClassNotFoundException 异常。

相关推荐
范中勤3 小时前
OSS 多文件打包回流上传:流式管道无落地 VS 临时磁盘落地,两种工业方案深度对比
java·高并发·zip·oss·架构选型
无毁的湖光-Al3 小时前
解Bug之路-隔离故障点后业务不恢复?-热点Key处理与亚稳态故障
java·数据库·mysql
Eyfcom3 小时前
快递管理系统中的电子围栏实战:高德与谷歌双平台技术选型与实现
java·后端·快递管理系统
Rain的Java大神之路3 小时前
Java PDF导出,90%的人选错库!11个生产坑+选型+代码,一次讲透
java·后端·面试
y1su3 小时前
Leetcode 单调栈模板
java·开发语言·数据结构·算法·leetcode
郑州光合科技余经理4 小时前
出海外卖平台:支付对接为什么要和下单收款分开
android·java·开发语言·前端·后端·php·ai编程
我是人✓4 小时前
Spring框架的事务管理
java·后端·spring
javachen__4 小时前
JsonView:一套VO实现多角色字段返回
java·linux·服务器
行者-全栈开发4 小时前
SpringBoot 生产环境踩坑实录:10个真实故障案例与教训
java·spring boot·后端
李福春4 小时前
斗破Google ADK Java苍穹-第7境斗皇-御天地-Harness治理
java·人工智能·python·深圳同盟·腾讯云架构师技术同盟·adk java·harness 治理