在 Java 中,Cloneable
接口 和 clone()
方法 是用于实现对象拷贝的机制。通过拷贝对象,你可以创建一个对象的副本,而不必手动复制每一个字段。Cloneable
是一个标记接口,用于表示该类支持对象的浅拷贝。但在实际开发中,除了浅拷贝,有时我们还需要深拷贝。
1. Cloneable
接口与 clone()
方法
Cloneable
接口
Cloneable
是一个标记接口,这意味着它没有任何方法,只是用来标记一个类支持对象拷贝。如果一个类实现了Cloneable
接口,则表示该类的对象可以被合法地克隆。- 如果你不实现
Cloneable
接口,调用clone()
方法时会抛出CloneNotSupportedException
异常。
clone()
方法
clone()
方法定义在Object
类中,默认实现的是浅拷贝。这意味着拷贝的对象中的所有基本数据类型都会被复制,而引用类型(对象引用)只复制它们的引用地址。
示例:实现 Cloneable
接口
class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 实现 clone 方法
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 调用父类的 clone() 方法
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("Alice", 30);
Person person2 = (Person) person1.clone(); // 浅拷贝
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
person1: Person{name='Alice', age=30}
person2: Person{name='Alice', age=30}
解释:
- 这里我们实现了
Cloneable
接口,并覆盖了clone()
方法。调用super.clone()
执行浅拷贝,person1
和person2
拥有相同的内容,但它们是两个不同的对象。
2. 浅拷贝(Shallow Copy)VS 深拷贝(Deep Copy)
2.1 浅拷贝(Shallow Copy)
浅拷贝是指:拷贝对象时,基本数据类型的字段会被复制,而引用类型的字段会拷贝引用地址。这意味着新对象和原对象共享相同的引用类型字段。如果引用类型对象被修改,那么两个对象都会受到影响。
示例:浅拷贝的问题
class Address {
String city;
public Address(String city) {
this.city = city;
}
}
class Person implements Cloneable {
String name;
int age;
Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", city=" + address.city + "}";
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Person person1 = new Person("Alice", 30, address);
Person person2 = (Person) person1.clone(); // 浅拷贝
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
// 修改 person2 的地址,person1 的地址也会改变
person2.address.city = "Los Angeles";
System.out.println("After modifying person2's city:");
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
person1: Person{name='Alice', age=30, city=New York}
person2: Person{name='Alice', age=30, city=New York}
After modifying person2's city:
person1: Person{name='Alice', age=30, city=Los Angeles}
person2: Person{name='Alice', age=30, city=Los Angeles}
解释:
- 在浅拷贝中,
address
是一个引用类型,它的地址被拷贝到了person2
。当我们修改person2
的address.city
时,person1
的address.city
也被修改了。这说明两个对象共享相同的地址引用,导致数据不独立。
2.2 深拷贝(Deep Copy)
深拷贝是指:不仅复制对象的基本数据类型,还要递归地复制引用类型字段,确保所有引用类型的对象都被完全复制,而不是共享引用。这样拷贝出来的对象完全独立,不受原对象的影响。
实现深拷贝的方法:
- 实现
clone()
方法时,手动调用引用对象的clone()
方法。 - 使用序列化和反序列化的方式进行深拷贝。
示例:实现深拷贝
我们在 Person
的 clone()
方法中,不仅要复制 Person
的基本字段,还要调用 Address
的 clone()
方法,确保 address
字段也被深拷贝。
class Address implements Cloneable {
String city;
public Address(String city) {
this.city = city;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
}
class Person implements Cloneable {
String name;
int age;
Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
// 调用父类的clone方法创建浅拷贝
Person clonedPerson = (Person) super.clone();
// 对引用类型进行深拷贝
clonedPerson.address = (Address) this.address.clone();
return clonedPerson;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", city=" + address.city + "}";
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Person person1 = new Person("Alice", 30, address);
Person person2 = (Person) person1.clone(); // 深拷贝
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
// 修改 person2 的地址,person1 的地址不会改变
person2.address.city = "Los Angeles";
System.out.println("After modifying person2's city:");
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
总结深拷贝的含义:
-
super.clone()
:调用Object
类的clone()
方法,对当前对象执行浅拷贝,复制所有基本类型字段和引用类型的引用。 -
clonedPerson.address = (Address) this.address.clone()
:对引用类型address
进行深拷贝,确保克隆后的对象和原对象引用不同的address
对象。 -
return clonedPerson
:返回深拷贝后的新对象。
person1: Person{name='Alice', age=30, city=New York}
person2: Person{name='Alice', age=30, city=New York}
After modifying person2's city:
person1: Person{name='Alice', age=30, city=New York}
person2: Person{name='Alice', age=30, city=Los Angeles}
3. 浅拷贝 VS 深拷贝的比较
Cloneable
接口用于指示一个类支持克隆操作,必须配合clone()
方法使用。- 浅拷贝会复制基本数据类型,但引用类型只复制地址,导致多个对象共享同一个引用类型字段,修改一个会影响另一个。
- 深拷贝递归地复制引用类型字段,确保拷贝出的对象与原对象完全独立。
- 实现深拷贝时,需要在
clone()
方法中手动调用引用对象的clone()
,或者使用序列化和反序列化的方法。