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
}
}
在这个例子中,original
和shallowClone
共享了相同的Address
对象,因此修改shallowClone
的会影响
original
的。
深克隆(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
}
}
在这个例子中,original
和deepClone
不再共享同一个Address
对象,因此修改deepClone
的不会影响
original
的。
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()
方法。
实现步骤:
- 实现
Cloneable
接口。 - 重写
clone()
方法。 - 在
clone()
方法中,调用()
来创建对象的浅克隆。 - 对于所有引用类型的成员变量,递归地调用
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 {
// 调用 () 进行浅克隆
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)
序列化是一种将对象的状态转换为字节流的过程,反序列化则是从字节流重构对象。这种方法通常用于对象的深克隆,因为它会将对象的整个状态(包括所有成员变量)写入字节流,并且通过字节流创建一个新的对象。
实现步骤:
- 使类实现
Serializable
接口。 - 使用
ObjectOutputStream
将对象写入字节数组流。 - 使用
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></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></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中的深克隆和浅克隆!