三分钟了解标记接口 Cloneable
三分钟了解标记接口 Cloneable
1. 接口定义
Cloneable 是一个标记接口(marker interface),没有定义任何方法。它的主要作用是表明某个类的对象可以通过 Object.clone() 方法进行克隆。
2. 用途
- 标记可克隆性:当一个类实现了
Cloneable接口时,表示该类允许通过Object.clone()方法创建其对象的副本。 - 避免异常:如果未实现
Cloneable接口而直接调用Object.clone()方法,会抛出CloneNotSupportedException异常。
3. 工作原理
Object.clone()是一个受保护的方法,默认情况下只能被子类调用。- 如果一个类实现了
Cloneable接口,Object.clone()方法会尝试创建并返回该对象的一个副本。 - 如果未实现
Cloneable接口,调用Object.clone()时会抛出CloneNotSupportedException。
4. 约定
- 按照惯例,实现
Cloneable接口的类应该重写Object.clone()方法,并将其声明为public,以便外部可以调用。 - 具体实现需要考虑深拷贝(Deep Copy)和浅拷贝(Shallow Copy)的区别。
5. 注意事项
- 接口中无
clone方法:Cloneable接口本身并不包含clone方法,因此不能仅凭实现该接口就保证对象可以被克隆。 - 反射调用不保证成功:即使通过反射调用
clone方法,也不能保证一定成功,因为克隆行为依赖于具体类的实现。
6. 示例
以下是一个实现 Cloneable 接口并重写 clone 方法的简单示例:
public class MyClass implements Cloneable {
private int value;
private Profession profession;
public MyClass(int value, Profession profession) {
this.value = value;
this.profession = profession;
}
public String toString() {
return "职业: " + profession.getName() + ", 价值: " + value;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) {
try {
Profession profession = new Profession("医生");
MyClass original = new MyClass(10, profession);
MyClass copy = (MyClass) original.clone();
System.out.println(original);
System.out.println(copy);
original.value = 20;
profession.setName("工程师");
System.out.println(original);
System.out.println(copy);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class Profession implements Cloneable {
@Getter @Setter
private String name;
public Profession(String name) {
this.name = name;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
打印内容:
初始-职业: 医生, 价值: 10
复制-职业: 医生, 价值: 10
初始-职业: 工程师, 价值: 20
复制-职业: 工程师, 价值: 10
可以观察到,默认的 clone() 方法复制对象时,基本类型字段的值会被直接复制,但对象引用字段只会复制引用(即新旧对象共享同一块内存中的子对象)。
实现深拷贝,需要递归复制所有引用对象。修改上述类的 clone 方法如下:
@Override
public Object clone() throws CloneNotSupportedException {
MyClass copy = (MyClass) super.clone();
copy.profession = (Profession) profession.clone(); // 递归克隆引用对象
return copy;
}
打印内容:
初始-职业: 医生, 价值: 10
复制-职业: 医生, 价值: 10
初始-职业: 工程师, 价值: 20
复制-职业: 医生, 价值: 10
浅拷贝与深拷贝的关键总结
| 特性 | 浅复制 | 深复制 |
|---|---|---|
| 实现方式 | 默认 Object.clone() |
需重写 clone() 并递归复制所有引用对象 |
| 引用对象共享 | 是(修改引用对象会影响原对象) | 否(新旧对象完全独立) |
| 安全性 | 低(易导致意外副作用) | 高(但实现复杂) |
7. 最佳实践
- **避免使用 `Cloneable`**:优先选择**不可变对象(Immutable Objects)** 或 **工厂方法/构造器** 创建新对象。
- **深复制的替代方案**:
- Apache Commons Lang 的 `SerializationUtils.clone()`(通过序列化实现深复制)。
- JSON 序列化/反序列化(如 Jackson/Gson)。
总结
Cloneable 接口的主要目的是作为标记接口,表明某个类支持克隆操作。然而,它本身并不提供克隆功能,具体的克隆逻辑需要由类自行实现(通常是重写 Object.clone() 方法)。需要注意的是,克隆行为可能涉及浅拷贝或深拷贝,开发者需根据需求选择合适的实现方式。

浙公网安备 33010602011771号