Background knowledge
在Java中,Cloneable 接口是一个标记接口(Marker Interface,它们内部都没有方法和属性),实现Cloneable接口表示该对象能被克隆,能使用Object.clone()方法。
要实现克隆功能,需要满足以下两个条件:
- 类实现了 Cloneable 接口。
- 在类中重写 clone() 方法。
What is Prototype Pattern
为了方便记忆,原型模式也可以叫克隆模式
。
原型模式通过复制现有对象来创建新对象,而无需显式地使用构造函数。原型模式允许动态创建对象,并且可以避免创建子类的复杂性。原型模式可以结合其他设计模式使用,例如工厂方法模式,以便更灵活地创建对象。
这个复制分为两种模式:
-
深复制
深克隆(Deep Clone),深克隆是指创建一个新对象,并将原始对象的所有成员变量(无论是值类型还是引用类型)的值都复制到新对象中,包括引用类型成员变量所引用的对象。这样,新对象和原始对象将拥有彼此独立的成员变量副本,彼此之间的修改不会相互影响。深克隆涉及到递归地复制对象及其引用对象的过程,因此可能会比较复杂和耗时。
一般考虑使用深克隆,就是因为有可变的引用类型
会受影响, 既然引用类型无法被完全克隆,那么我们可以考虑在引用类型所在的类也实现Cloneable接口。有多少个引用类型,就去实现多少个Cloneable接口。 -
浅复制
浅克隆(Shallow Clone),浅克隆是指创建一个新对象,并将原始对象的非引用类型成员变量的值复制到新对象中。对于引用类型成员变量,浅克隆将复制引用,使新对象和原始对象共享相同的引用对象。这意味着在浅克隆后,如果修改其中一个对象的引用类型成员变量,将会影响到另一个对象。因此,浅克隆只复制对象的表面结构,而不涉及引用对象本身的复制。浅拷贝的情况下,原被克隆对象发生变化后,克隆对象的
基本数据类型
和不可变引用数据类型(String)
的数据不会发生影响,而一些其他字段为可变的应用类型,只要克隆对象的内容随着被克隆对象的变化发生了同样的变化,说明两个对象的属性字段指向同一个引用,才会造成这样的结局。(我就碰到过因为对象被同事插进来的代码导致对象发生了变更,代码出现BUG的问题,后面是使用的深拷贝才消除同事的代码对该对象的影响)。如果克隆的对象内没有可变的引用对象,都是只用使用一些基础类型,那么直接用浅拷贝即可。
原型模式可以实现深克隆或浅克隆,具体实现取决于需求。
配合其他设计模式:
原型模式可以结合其他设计模式使用,例如工厂方法模式,以便更灵活地创建对象。
Key elements
- 抽象原型类
- 具体实现类
- Client
Example of Draw shape —— shallow clone
以下是一个简单的浅克隆例子。
抽象原型类:
abstract class Shape implements Cloneable {
protected String type;
abstract void draw();
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
具体实现类:
// 具体原型类 - Rectangle
class Rectangle extends Shape {
public Rectangle() {
this.type = "Rectangle";
}
public void draw() {
System.out.println("Drawing a rectangle.");
}
}
// 具体原型类 - Circle
class Circle extends Shape {
public Circle() {
this.type = "Circle";
}
public void draw() {
System.out.println("Drawing a circle.");
}
}
Client:
// 客户端代码
public class Main {
public static void main(String[] args) {
Shape rectangle = new Rectangle();
Shape circle = new Circle();
Shape clonedRectangle = (Shape) rectangle.clone();
Shape clonedCircle = (Shape) circle.clone();
System.out.println("Original Rectangle Type: " + rectangle.type);
rectangle.draw(); // Drawing a rectangle.
System.out.println("Cloned Rectangle Type: " + clonedRectangle.type);
clonedRectangle.draw(); // Drawing a rectangle.
System.out.println("Original Circle Type: " + circle.type);
circle.draw(); // Drawing a circle.
System.out.println("Cloned Circle Type: " + clonedCircle.type);
clonedCircle.draw(); // Drawing a circle.
}
}
在上面的示例中,我们定义了抽象原型类 Shape,它包含了 clone() 方法和 draw() 方法。然后,我们实现了具体的原型类 Rectangle 和 Circle,它们继承自 Shape 并实现了相应的方法。
在客户端代码中,我们首先创建了一个原始的 Rectangle 对象和一个原始的 Circle 对象。然后,我们分别对它们进行克隆,并得到克隆后的 Shape 对象。
通过原型模式,我们可以通过克隆(浅克隆)现有对象来创建新对象,而无需显式地调用构造函数。这消除了重复创建相似对象的需要,并提高了性能。
Reference
深克隆请参考这个文章
https://cloud.tencent.com/developer/article/1628044