Java中的深克隆与浅克隆

Java中的深克隆与浅克隆

在Java编程中,复制对象有两种方式:浅克隆(Shallow Copy)深克隆(Deep Copy)。这两种方式在处理对象的成员变量,尤其是引用类型的成员变量时,有着截然不同的行为。在这篇博客中,我们将详细探讨它们的区别、实现方式以及应用场景。

浅克隆(Shallow Copy)

浅克隆是创建一个新对象,这个新对象的所有成员变量都复制了原对象的对应成员变量的值。对于基本数据类型,浅克隆会复制值;对于引用类型,浅克隆复制的是引用地址,这意味着浅克隆的对象与原对象共享相同的引用对象。

特点
  • 基本数据类型:直接复制值。
  • 引用数据类型:复制引用地址,因此原对象和克隆对象中的引用变量指向同一个内存地址。改变其中一个对象的引用变量会影响到另一个对象。
实现方式

在Java中,Object类的clone()方法提供了浅克隆的默认实现。要实现浅克隆,需要让类实现Cloneable接口,并重写clone()方法。

示例代码

class Person implements Cloneable {
    String name;
    Address address;

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

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

class Address {
    String city;

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

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person original = new Person("John", address);
        Person shallowClone = (Person) original.clone();

        System.out.println(original.address.city); // New York
        System.out.println(shallowClone.address.city); // New York

        // 改变shallowClone的address属性
        shallowClone.address.city = "Los Angeles";
        System.out.println(original.address.city); // Los Angeles
        System.out.println(shallowClone.address.city); // Los Angeles
    }
}

在这个例子中,originalshallowClone共享了相同的Address对象,因此修改shallowCloneaddress.city会影响originaladdress.city

深克隆(Deep Copy)

深克隆是指创建一个新对象,复制原对象的所有成员变量,包括递归地复制所有引用对象的内容,最终使得新对象和原对象完全独立,不共享任何引用对象。

特点
  • 基本数据类型:深克隆同样会复制值。
  • 引用数据类型:深克隆会递归地创建新对象并复制内容,确保克隆对象与原对象之间完全没有共享的引用。
实现方式

深克隆需要手动实现。通常,通过重写clone()方法并在其中手动调用每个引用类型的clone()方法来实现深克隆,或者通过序列化与反序列化实现。

示例代码

class Person implements Cloneable {
    String name;
    Address address;

    public 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;
    }
}

class Address implements Cloneable {
    String city;

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

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

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person original = new Person("John", address);
        Person deepClone = (Person) original.clone();

        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // New York

        // 改变deepClone的address属性
        deepClone.address.city = "Los Angeles";
        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // Los Angeles
    }
}

在这个例子中,originaldeepClone不再共享同一个Address对象,因此修改deepCloneaddress.city不会影响originaladdress.city

String类型在克隆中的特殊性

String类型是Java中的一个特殊类型,它是不可变对象(immutable)。这意味着一旦String对象被创建,它的内容就不能再改变。因此,无论是浅克隆还是深克隆,String类型的行为都相同:不会因为其中一个对象的修改而影响到另一个对象。

String类型在浅克隆中的行为

由于String不可变,当你执行浅克隆时,String对象的引用被复制,而不是String的内容本身被复制。这确保了即使两个对象共享同一个String实例,String的不可变性也不会导致数据不一致。

示例代码

class Person implements Cloneable {
    String name;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 浅克隆
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person original = new Person("John");
        Person shallowClone = (Person) original.clone();

        System.out.println(original.name); // John
        System.out.println(shallowClone.name); // John

        // 改变 shallowClone 的 name 引用
        shallowClone.name = "Doe";
        System.out.println(original.name); // John
        System.out.println(shallowClone.name); // Doe
    }
}
String类型在深克隆中的行为

在深克隆中,String的行为基本上与浅克隆一致,因为String是不可变的。因此,深克隆也不需要为String类型创建新的副本。

示例代码

class Person implements Cloneable {
    String name;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone(); // 深克隆
        cloned.name = new String(this.name); // 实际上不需要
        return cloned;
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person original = new Person("John");
        Person deepClone = (Person) original.clone();

        System.out.println(original.name); // John
        System.out.println(deepClone.name); // John

        // 改变 deepClone 的 name 引用
        deepClone.name = "Doe";
        System.out.println(original.name); // John
        System.out.println(deepClone.name); // Doe
    }
}

实现深克隆的几种常用方法

在Java中,实现深克隆有几种常用的方法,下面我们来看一下几种主要的实现方式。

1. 重写clone()方法

通过实现Cloneable接口并重写Object类的clone()方法,可以实现对象的深克隆。这种方法要求所有需要深克隆的成员变量的类也要实现Cloneable接口并重写clone()方法。

实现步骤:

  1. 实现Cloneable接口。
  2. 重写clone()方法。
  3. clone()方法中,调用super.clone()来创建对象的浅克隆。
  4. 对于所有引用类型的成员变量,递归地调用clone()方法进行深克隆。

示例代码

class Address implements Cloneable {
    String city;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // Address 也实现了 Cloneable 接口
    }
}

class Person implements Cloneable {
    String name;
    Address address;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 调用 super.clone() 进行浅克隆
        Person cloned = (Person) super.clone();
        // 手动深克隆引用类型成员变量
        cloned.address = (Address) address.clone();
        return cloned;
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person original = new Person("John", address);
        Person deepClone = (Person) original.clone();

        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // New York

        // 改变 deepClone 的 address 属性
        deepClone.address.city = "Los Angeles";
        System

.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // Los Angeles
    }
}
2. 使用序列化(Serialization)

序列化是一种将对象的状态转换为字节流的过程,反序列化则是从字节流重构对象。这种方法通常用于对象的深克隆,因为它会将对象的整个状态(包括所有成员变量)写入字节流,并且通过字节流创建一个新的对象。

实现步骤:

  1. 使类实现Serializable接口。
  2. 使用ObjectOutputStream将对象写入字节数组流。
  3. 使用ObjectInputStream从字节数组流中读取对象。

示例代码

import java.io.*;

class Address implements Serializable {
    String city;

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

class Person implements Serializable {
    String name;
    Address address;

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

    // 深克隆方法,使用序列化
    public Person deepClone() throws IOException, ClassNotFoundException {
        // 序列化对象到字节流
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);
        objectOutputStream.flush();
        objectOutputStream.close();

        // 从字节流中反序列化对象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        return (Person) objectInputStream.readObject();
    }
}

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Address address = new Address("New York");
        Person original = new Person("John", address);
        Person deepClone = original.deepClone();

        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // New York

        // 改变 deepClone 的 address 属性
        deepClone.address.city = "Los Angeles";
        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // Los Angeles
    }
}
3. 使用第三方库(如Apache Commons Lang)

Apache Commons Lang库提供了一些工具类,可以方便地进行深克隆。SerializationUtils类可以用来实现对象的深克隆。这个方法和使用Java自带的序列化方法相似,但是使用了更简洁的库方法。

示例代码

首先,确保在项目中添加了Apache Commons Lang库的依赖。

<!-- Maven dependency -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

然后在代码中使用SerializationUtils类:

import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;

class Address implements Serializable {
    String city;

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

class Person implements Serializable {
    String name;
    Address address;

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

public class Main {
    public static void main(String[] args) {
        Address address = new Address("New York");
        Person original = new Person("John", address);

        // 使用 SerializationUtils 进行深克隆
        Person deepClone = SerializationUtils.clone(original);

        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // New York

        // 改变 deepClone 的 address 属性
        deepClone.address.city = "Los Angeles";
        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // Los Angeles
    }
}
4. 使用JSON序列化

通过将对象序列化为JSON格式的字符串,然后再从JSON字符串反序列化为对象,也可以实现深克隆。这种方法依赖于第三方JSON库(如Gson、Jackson等)。

示例代码

使用Gson库实现深克隆:

首先,确保在项目中添加了Gson库的依赖。

<!-- Maven dependency -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.9</version>
</dependency>

然后在代码中使用Gson库:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

class Address {
    String city;

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

class Person {
    String name;
    Address address;

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

    // 深克隆方法,使用 JSON 序列化
    public Person deepClone() {
        Gson gson = new GsonBuilder().create();
        String json = gson.toJson(this);
        return gson.fromJson(json, Person.class);
    }
}

public class Main {
    public static void main(String[] args) {
        Address address = new Address("New York");
        Person original = new Person("John", address);
        Person deepClone = original.deepClone();

        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // New York

        // 改变 deepClone 的 address 属性
        deepClone.address.city = "Los Angeles";
        System.out.println(original.address.city); // New York
        System.out.println(deepClone.address.city); // Los Angeles
    }
}

总结

  • 浅克隆:复制对象时不复制引用对象的内容,克隆对象和原对象共享引用对象。
  • 深克隆:复制对象时也复制引用对象的内容,克隆对象和原对象完全独立。
  • 实现深克隆的方式:重写clone()方法、使用序列化、使用第三方库(如Apache Commons Lang)、使用JSON序列化。

了解深克隆和浅克隆的区别,并根据实际需要选择合适的克隆方式,可以帮助开发者更好地管理对象的复制和状态。希望这篇博客能够帮助你更好地理解Java中的深克隆和浅克隆!

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值