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
方法。克隆对象person2
的address
字段与原始对象person1
共享同一个引用对象。因此,修改person2
的address
字段会影响到person1
的address
字段。
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
字段进行深拷贝,确保person1
和person2
拥有独立的引用对象。
使用序列化和反序列化实现深拷贝
另一种实现深拷贝的方法是使用序列化和反序列化。此方法适用于对象及其所有字段都实现了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
对象的深拷贝。序列化将对象转换为字节流,反序列化将字节流转换为对象,从而创建对象的深拷贝。