深浅克隆学习笔记

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 对象 person1person2。通过引用对象 person1person2,我们可以访问和修改这两个对象的属性。

需要注意的是,在 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 的地址也会相应地发生变化。

然后,我们展示了深克隆的示例。通过在 Personclone() 方法中调用 addressclone() 方法,我们创建了一个新的 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 异常。此外,还要注意处理可能抛出的 IOExceptionClassNotFoundException 异常。

相关推荐
探索java21 分钟前
Java并发编程中的StampedLock详解:原理、实践与性能优化
java·stampedlock
界面开发小八哥32 分钟前
「Java EE开发指南」如何用MyEclipse将Java项目转换为Web项目?
java·ide·java-ee·eclipse·开发工具·myeclipse
pobu16843 分钟前
aksk前端签名实现
java·前端·javascript
一个天蝎座 白勺 程序猿1 小时前
飞算JavaAI进阶:重塑Java开发范式的AI革命
java·开发语言·人工智能
前端 贾公子1 小时前
tailwindCSS === 使用插件自动类名排序
java·开发语言
没有bug.的程序员1 小时前
JAVA面试宝典 -《Spring Boot 自动配置魔法解密》
java·spring boot·面试
hnlucky2 小时前
《Nginx + 双Tomcat实战:域名解析、静态服务与反向代理、负载均衡全指南》
java·linux·服务器·前端·nginx·tomcat·web
hnlucky2 小时前
同时部署两个不同版本的tomcat要如何配置环境变量
java·服务器·http·tomcat·web
yngsqq3 小时前
netdxf—— CAD c#二次开发之(netDxf 处理 DXF 文件)
java·前端·c#